/*++ * @name FsRtlDoesDbcsContainWildCards * @implemented * * Returns TRUE if the given DbcsName contains wildcards such as *, ?, * ANSI_DOS_STAR, ANSI_DOS_DOT, and ANSI_DOS_QM * * @param Name * The Name to check * * @return TRUE if there are wildcards, FALSE otherwise * * @remarks None * *--*/ BOOLEAN NTAPI FsRtlDoesDbcsContainWildCards(IN PANSI_STRING Name) { USHORT i; PAGED_CODE(); /* Check every character */ for (i = 0; i < Name->Length; i++) { /* First make sure it's not the Lead DBCS */ if (FsRtlIsLeadDbcsCharacter(Name->Buffer[i])) { i++; } else if (FsRtlIsAnsiCharacterWild(Name->Buffer[i])) { /* Now return if it has a wildcard */ return TRUE; } } /* We didn't return above...so none found */ return FALSE; }
/*++ * @name FsRtlDissectDbcs * @implemented * * Dissects a given path name into first and remaining part. * * @param Name * ANSI string to dissect. * * @param FirstPart * Pointer to user supplied ANSI_STRING, that will later point * to the first part of the original name. * * @param RemainingPart * Pointer to user supplied ANSI_STRING, that will later point * to the remaining part of the original name. * * @return None * * @remarks Example: * Name: \test1\test2\test3 * FirstPart: test1 * RemainingPart: test2\test3 * *--*/ VOID NTAPI FsRtlDissectDbcs(IN ANSI_STRING Name, OUT PANSI_STRING FirstPart, OUT PANSI_STRING RemainingPart) { USHORT FirstPosition, i; USHORT SkipFirstSlash = 0; PAGED_CODE(); /* Zero the strings before continuing */ RtlZeroMemory(FirstPart, sizeof(ANSI_STRING)); RtlZeroMemory(RemainingPart, sizeof(ANSI_STRING)); /* Just quit if the string is empty */ if (!Name.Length) return; /* Find first backslash */ FirstPosition = Name.Length; for (i = 0; i < Name.Length; i++) { /* First make sure the character it's not the Lead DBCS */ if (FsRtlIsLeadDbcsCharacter(Name.Buffer[i])) { i++; } /* If we found one... */ else if (Name.Buffer[i] == '\\') { /* If it begins string, just notice it and continue */ if (i == 0) { SkipFirstSlash = 1; } else { /* Else, save its position and break out of the loop */ FirstPosition = i; break; } } } /* Set up the first result string */ FirstPart->Buffer = Name.Buffer + SkipFirstSlash; FirstPart->Length = (FirstPosition - SkipFirstSlash); FirstPart->MaximumLength = FirstPart->Length; /* And second one, if necessary */ if (FirstPosition < (Name.Length)) { RemainingPart->Buffer = Name.Buffer + FirstPosition + 1; RemainingPart->Length = Name.Length - (FirstPosition + 1); RemainingPart->MaximumLength = RemainingPart->Length; } }
/*++ * @name FsRtlIsHpfsDbcsLegal * @implemented * * Returns TRUE if the given DbcsName is a valid HPFS filename * * @param DbcsName * The filename to check. It can also contains pathname. * * @param WildCardsPermissible * If this is set to FALSE and if filename contains wildcard, the function * will fail * * @param PathNamePermissible * If this is set to FALSE and if the filename comes with a pathname, the * function will fail * * @param LeadingBackslashPermissible * If this is set to FALSE and if the filename starts with a backslash, the * function will fail * * @return TRUE if the DbcsName is legal, FALSE otherwise * * @remarks None * *--*/ BOOLEAN NTAPI FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName, IN BOOLEAN WildCardsPermissible, IN BOOLEAN PathNamePermissible, IN BOOLEAN LeadingBackslashPermissible) { ANSI_STRING FirstPart, RemainingPart, Name; USHORT i; PAGED_CODE(); /* Just quit if the string is empty */ if (!DbcsName.Length) return FALSE; /* DbcsName wasn't supposed to be started with \ */ if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\') return FALSE; /* DbcsName was allowed to be started with \, but now, remove it */ else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\') { DbcsName.Buffer = DbcsName.Buffer + 1; DbcsName.Length = DbcsName.Length - 1; DbcsName.MaximumLength = DbcsName.MaximumLength - 1; } /* Extract first part of the DbcsName to work on */ FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart); while (FirstPart.Length > 0) { /* Accept special filename if wildcards are allowed */ if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.') { if (FirstPart.Length == 2) { if (FirstPart.Buffer[1] == '.') { goto EndLoop; } } else { goto EndLoop; } } /* Filename must be 255 bytes maximum */ if (FirstPart.Length > 255) return FALSE; /* Now, we will parse the filename to find everything bad in */ for (i = 0; i < FirstPart.Length; i++) { /* First make sure the character it's not the Lead DBCS */ if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i])) { if (i == (FirstPart.Length) - 1) return FALSE; i++; } /* Then check for bad characters */ else if (!FsRtlIsAnsiCharacterLegalHpfs(FirstPart.Buffer[i], WildCardsPermissible)) { return FALSE; } } /* Filename mustn't finish with a space or a dot */ if ((FirstPart.Buffer[FirstPart.Length - 1] == ' ') || (FirstPart.Buffer[FirstPart.Length - 1] == '.')) return FALSE; EndLoop: /* Preparing next loop */ Name.Buffer = RemainingPart.Buffer; Name.Length = RemainingPart.Length; Name.MaximumLength = RemainingPart.MaximumLength; /* Call once again our dissect function */ FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart); /* We found a pathname, it wasn't allowed */ if (FirstPart.Length > 0 && !PathNamePermissible) return FALSE; } return TRUE; }
VOID Fat8dot3ToString ( IN PIRP_CONTEXT IrpContext, IN PDIRENT Dirent, IN BOOLEAN RestoreCase, OUT POEM_STRING OutputString ) /*++ Routine Description: Convert fat 8.3 format into a string. The 8.3 name must be well formed. Arguments: Dirent - Supplies the input 8.3 name to convert RestoreCase - If TRUE, then the magic reserved bits are used to restore the original case. OutputString - Receives the converted name, the memory must be supplied by the caller. Return Value: None --*/ { ULONG DirentIndex, StringIndex; ULONG BaseLength, ExtensionLength; DebugTrace(+1, Dbg, "Fat8dot3ToString\n", 0); // // First, find the length of the base component. // for (BaseLength = 8; BaseLength > 0; BaseLength -= 1) { if (Dirent->FileName[BaseLength - 1] != UCHAR_SP) { break; } } // // Now find the length of the extension. // for (ExtensionLength = 3; ExtensionLength > 0; ExtensionLength -= 1) { if (Dirent->FileName[8 + ExtensionLength - 1] != UCHAR_SP) { break; } } // // If there was a base part, copy it and check the case. Don't forget // if the first character needs to be changed from 0x05 to 0xe5. // if (BaseLength != 0) { RtlCopyMemory( OutputString->Buffer, Dirent->FileName, BaseLength ); if (OutputString->Buffer[0] == FAT_DIRENT_REALLY_0E5) { OutputString->Buffer[0] = (CHAR)0xe5; } // // Now if we are to restore case, look for A-Z // if (FatData.ChicagoMode && RestoreCase && FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) { for (StringIndex = 0; StringIndex < BaseLength; StringIndex += 1) { // // Depending on whether the media was built in a system that was // running with "code page invariance" (see FatEvaluateNameCase), // there could be double-byte OEM characters lying in wait here. // Gotta skip them. // if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) { StringIndex += 1; continue; } if ((OutputString->Buffer[StringIndex] >= 'A') && (OutputString->Buffer[StringIndex] <= 'Z')) { OutputString->Buffer[StringIndex] += 'a' - 'A'; } } } } // // If there was an extension, copy that over. Else we now know the // size of the string. // if (ExtensionLength != 0) { PUCHAR o, d; // // Now add the dot // OutputString->Buffer[BaseLength++] = '.'; // // Copy over the extension into the output buffer. // o = &OutputString->Buffer[BaseLength]; d = &Dirent->FileName[8]; switch (ExtensionLength) { case 3: *o++ = *d++; case 2: *o++ = *d++; case 1: *o++ = *d++; } // // Set the output string length // OutputString->Length = (USHORT)(BaseLength + ExtensionLength); // // Now if we are to restore case, look for A-Z // if (FatData.ChicagoMode && RestoreCase && FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) { for (StringIndex = BaseLength; StringIndex < OutputString->Length; StringIndex++ ) { // // Depending on whether the media was built in a system that was // running with "code page invariance" (see FatEvaluateNameCase), // there could be double-byte OEM characters lying in wait here. // Gotta skip them. // if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) { StringIndex += 1; continue; } if ((OutputString->Buffer[StringIndex] >= 'A') && (OutputString->Buffer[StringIndex] <= 'Z')) { OutputString->Buffer[StringIndex] += 'a' - 'A'; } } } } else { // // Set the output string length // OutputString->Length = (USHORT)BaseLength; } // // And return to our caller // DebugTrace(-1, Dbg, "Fat8dot3ToString, OutputString = \"%Z\" -> VOID\n", OutputString); UNREFERENCED_PARAMETER( IrpContext ); return; }
NTSTATUS FatSetFsLabelInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_LABEL_INFORMATION Buffer ) /*++ Routine Description: This routine implements the set volume label call Arguments: Vcb - Supplies the Vcb being queried Buffer - Supplies the input where the information is stored. Return Value: NTSTATUS - Returns the status for the operation --*/ { NTSTATUS Status; PDIRENT Dirent; PBCB DirentBcb = NULL; ULONG ByteOffset; WCHAR TmpBuffer[11]; UCHAR OemBuffer[11]; OEM_STRING OemLabel; UNICODE_STRING UnicodeString; UNICODE_STRING UpcasedLabel; DebugTrace(+1, Dbg, "FatSetFsLabelInfo...\n", 0); // // Setup our local variable // UnicodeString.Length = (USHORT)Buffer->VolumeLabelLength; UnicodeString.MaximumLength = UnicodeString.Length; UnicodeString.Buffer = (PWSTR) &Buffer->VolumeLabel[0]; // // Make sure the name can fit into the stack buffer // if ( UnicodeString.Length > 11*sizeof(WCHAR) ) { return STATUS_INVALID_VOLUME_LABEL; } // // Upcase the name and convert it to the Oem code page. // OemLabel.Buffer = &OemBuffer[0]; OemLabel.Length = 0; OemLabel.MaximumLength = 11; Status = RtlUpcaseUnicodeStringToCountedOemString( &OemLabel, &UnicodeString, FALSE ); // // Volume label that fits in 11 unicode character length limit // is not necessary within 11 characters in OEM character set. // if (!NT_SUCCESS( Status )) { DebugTrace(-1, Dbg, "FatSetFsLabelInfo: Label must be too long. %08lx\n", Status ); return STATUS_INVALID_VOLUME_LABEL; } // // Strip spaces off of the label. // if (OemLabel.Length > 0) { USHORT i; USHORT LastSpaceIndex = MAXUSHORT; // // Check the label for illegal characters // for ( i = 0; i < (ULONG)OemLabel.Length; i += 1 ) { if ( FsRtlIsLeadDbcsCharacter( OemLabel.Buffer[i] ) ) { LastSpaceIndex = MAXUSHORT; i += 1; continue; } if (!FsRtlIsAnsiCharacterLegalFat(OemLabel.Buffer[i], FALSE) || (OemLabel.Buffer[i] == '.')) { return STATUS_INVALID_VOLUME_LABEL; } // // Watch for the last run of spaces, so we can strip them. // if (OemLabel.Buffer[i] == ' ' && LastSpaceIndex == MAXUSHORT) { LastSpaceIndex = i; } else { LastSpaceIndex = MAXUSHORT; } } if (LastSpaceIndex != MAXUSHORT) { OemLabel.Length = LastSpaceIndex; } } // // Get the Unicode upcased string to store in the VPB. // UpcasedLabel.Length = UnicodeString.Length; UpcasedLabel.MaximumLength = 11*sizeof(WCHAR); UpcasedLabel.Buffer = &TmpBuffer[0]; Status = RtlOemStringToCountedUnicodeString( &UpcasedLabel, &OemLabel, FALSE ); if (!NT_SUCCESS( Status )) { DebugTrace(-1, Dbg, "FatSetFsLabelInfo: Label must be too long. %08lx\n", Status ); return STATUS_INVALID_VOLUME_LABEL; } DirentBcb = NULL; // // Make this look like a write through to disk. This is important to // avoid a unpleasant window where it looks like we have the wrong volume. // SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); try { // // Are we setting or removing the label? Note that shaving spaces could // make this different than wondering if the input buffer is non-zero length. // if (OemLabel.Length > 0) { // // Locate the volume label if there already is one // FatLocateVolumeLabel( IrpContext, Vcb, &Dirent, &DirentBcb, &ByteOffset ); // // Check that we really got one, if not then we need to create // a new one. The procedure we call will raise an appropriate // status if we are not able to allocate a new dirent // if (Dirent == NULL) { ByteOffset = FatCreateNewDirent( IrpContext, Vcb->RootDcb, 1 ); FatPrepareWriteDirectoryFile( IrpContext, Vcb->RootDcb, ByteOffset, sizeof(DIRENT), &DirentBcb, &Dirent, FALSE, TRUE, &Status ); ASSERT( NT_SUCCESS( Status )); } else { // // Just mark this guy dirty now. // FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); } // // Now reconstruct the volume label dirent. // FatConstructLabelDirent( IrpContext, Dirent, &OemLabel ); // // Unpin the Bcb here so that we will get any IO errors // here before changing the VPB label. // FatUnpinBcb( IrpContext, DirentBcb ); FatUnpinRepinnedBcbs( IrpContext ); // // Now set the upcased label in the VPB // RtlCopyMemory( &Vcb->Vpb->VolumeLabel[0], &UpcasedLabel.Buffer[0], UpcasedLabel.Length ); Vcb->Vpb->VolumeLabelLength = UpcasedLabel.Length; } else { // // Otherwise we're trying to delete the label // Locate the current volume label if there already is one // FatLocateVolumeLabel( IrpContext, Vcb, &Dirent, &DirentBcb, &ByteOffset ); // // Check that we really got one // if (Dirent == NULL) { try_return( Status = STATUS_SUCCESS ); } // // Now delete the current label. // Dirent->FileName[0] = FAT_DIRENT_DELETED; ASSERT( (Vcb->RootDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || RtlAreBitsSet( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap, ByteOffset / sizeof(DIRENT), 1 ) ); RtlClearBits( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap, ByteOffset / sizeof(DIRENT), 1 ); FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); // // Unpin the Bcb here so that we will get any IO errors // here before changing the VPB label. // FatUnpinBcb( IrpContext, DirentBcb ); FatUnpinRepinnedBcbs( IrpContext ); // // Now set the label in the VPB // Vcb->Vpb->VolumeLabelLength = 0; } Status = STATUS_SUCCESS; FatSortDirectory(IrpContext, Vcb->RootDcb); try_exit: NOTHING; } finally { DebugUnwind( FatSetFsALabelInfo ); FatUnpinBcb( IrpContext, DirentBcb ); DebugTrace(-1, Dbg, "FatSetFsALabelInfo -> STATUS_SUCCESS\n", 0); } return Status; }