NTSTATUS CdQueryAlternateNameInfo ( IN PIRP_CONTEXT IrpContext, IN PFCB Fcb, IN PCCB Ccb, IN OUT PFILE_NAME_INFORMATION Buffer, IN OUT PULONG Length ) /*++ Routine Description: This routine performs the query alternate name information function. We lookup the dirent for this file and then check if there is a short name. Arguments: Fcb - Supplies the Fcb being queried, it has been verified. Ccb - Ccb for this open handle. Buffer - Supplies a pointer to the buffer where the information is to be returned. Length - Supplies the length of the buffer in bytes, and receives the remaining bytes free in the buffer upon return. Return Value: NTSTATUS - STATUS_SUCCESS if the whole name would fit into the user buffer, STATUS_OBJECT_NAME_NOT_FOUND if we can't return the name, STATUS_BUFFER_OVERFLOW otherwise. --*/ { NTSTATUS Status = STATUS_SUCCESS; DIRENT_ENUM_CONTEXT DirContext; DIRENT Dirent; PUNICODE_STRING NameToUse; ULONG DirentOffset; COMPOUND_PATH_ENTRY CompoundPathEntry; FILE_ENUM_CONTEXT FileContext; PFCB ParentFcb; BOOLEAN ReleaseParentFcb = FALSE; BOOLEAN CleanupFileLookup = FALSE; BOOLEAN CleanupDirectoryLookup = FALSE; WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / 2 ]; USHORT ShortNameLength; PAGED_CODE(); // // Initialize the buffer length to zero. // Buffer->FileNameLength = 0; // // If this is the root or this file was opened using a version number then // there is no short name. // if ((Fcb == Fcb->Vcb->RootIndexFcb) || FlagOn( Ccb->Flags, CCB_FLAG_OPEN_WITH_VERSION)) { return STATUS_OBJECT_NAME_NOT_FOUND; } // // Use a try-finally to cleanup the structures. // try { ParentFcb = Fcb->ParentFcb; CdAcquireFileShared( IrpContext, ParentFcb ); ReleaseParentFcb = TRUE; // // Do an unsafe test to see if we need to create a file object. // if (ParentFcb->FileObject == NULL) { CdCreateInternalStream( IrpContext, ParentFcb->Vcb, ParentFcb ); } if (CdFidIsDirectory( Fcb->FileId)) { // // Fcb is for a directory, so we need to dig the dirent from the parent. In // order to do this we need to get the name of the directory from its pathtable // entry and then search in the parent for a matching dirent. // // This could be optimized somewhat. // CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); CdInitializeFileContext( IrpContext, &FileContext ); CleanupDirectoryLookup = TRUE; CdLookupPathEntry( IrpContext, CdQueryFidPathTableOffset( Fcb->FileId ), Fcb->Ordinal, FALSE, &CompoundPathEntry ); CdUpdatePathEntryName( IrpContext, &CompoundPathEntry.PathEntry, TRUE ); if (!CdFindDirectory( IrpContext, ParentFcb, &CompoundPathEntry.PathEntry.CdCaseDirName, TRUE, &FileContext )) { // // If we failed to find the child directory by name in the parent // something is quite wrong with this disc. // CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); } NameToUse = &FileContext.InitialDirent->Dirent.CdCaseFileName.FileName; DirentOffset = FileContext.InitialDirent->Dirent.DirentOffset; } else { // // Initialize the search dirent structures. // CdInitializeDirContext( IrpContext, &DirContext ); CdInitializeDirent( IrpContext, &Dirent ); CleanupFileLookup = TRUE; CdLookupDirent( IrpContext, ParentFcb, CdQueryFidDirentOffset( Fcb->FileId ), &DirContext ); CdUpdateDirentFromRawDirent( IrpContext, ParentFcb, &DirContext, &Dirent ); // // Now update the dirent name. // CdUpdateDirentName( IrpContext, &Dirent, TRUE ); NameToUse = &Dirent.CdCaseFileName.FileName; DirentOffset = Dirent.DirentOffset; } // // If the name is 8.3 then fail this request. // if (CdIs8dot3Name( IrpContext, *NameToUse )) { try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND ); } CdGenerate8dot3Name( IrpContext, NameToUse, DirentOffset, ShortNameBuffer, &ShortNameLength ); // // We now have the short name. We have left it in Unicode form so copy it directly. // Buffer->FileNameLength = ShortNameLength; if (Buffer->FileNameLength + sizeof( ULONG ) > *Length) { Buffer->FileNameLength = *Length - sizeof( ULONG ); Status = STATUS_BUFFER_OVERFLOW; } RtlCopyMemory( Buffer->FileName, ShortNameBuffer, Buffer->FileNameLength ); try_exit: NOTHING; } finally { if (CleanupFileLookup) { CdCleanupDirContext( IrpContext, &DirContext ); CdCleanupDirent( IrpContext, &Dirent ); } else if (CleanupDirectoryLookup) { CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); CdCleanupFileContext( IrpContext, &FileContext ); } if (ReleaseParentFcb) { CdReleaseFile( IrpContext, ParentFcb ); } } // // Reduce the available bytes by the amount stored into this buffer. // if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { *Length -= sizeof( ULONG ) + Buffer->FileNameLength; } return Status; }
VOID CdInitializeEnumeration ( IN PIRP_CONTEXT IrpContext, IN PIO_STACK_LOCATION IrpSp, IN PFCB Fcb, IN OUT PCCB Ccb, IN OUT PFILE_ENUM_CONTEXT FileContext, OUT PBOOLEAN ReturnNextEntry, OUT PBOOLEAN ReturnSingleEntry, OUT PBOOLEAN InitialQuery ) /*++ Routine Description: This routine is called to initialize the enumeration variables and structures. We look at the state of a previous enumeration from the Ccb as well as any input values from the user. On exit we will position the FileContext at a file in the directory and let the caller know whether this entry or the next entry should be returned. Arguments: IrpSp - Irp stack location for this request. Fcb - Fcb for this directory. Ccb - Ccb for the directory handle. FileContext - FileContext to use for this enumeration. ReturnNextEntry - Address to store whether we should return the entry at the FileContext position or the next entry. ReturnSingleEntry - Address to store whether we should only return a single entry. InitialQuery - Address to store whether this is the first enumeration query on this handle. Return Value: None. --*/ { NTSTATUS Status; PUNICODE_STRING FileName; CD_NAME WildCardName; CD_NAME SearchExpression; ULONG CcbFlags; ULONG DirentOffset; ULONG LastDirentOffset; BOOLEAN KnownOffset; BOOLEAN Found; PAGED_CODE(); // // If this is the initial query then build a search expression from the input // file name. // if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) { FileName = IrpSp->Parameters.QueryDirectory.FileName; CcbFlags = 0; // // If the filename is not specified or is a single '*' then we will // match all names. // if ((FileName == NULL) || (FileName->Buffer == NULL) || (FileName->Length == 0) || ((FileName->Length == sizeof( WCHAR )) && (FileName->Buffer[0] == L'*'))) { SetFlag( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL ); RtlZeroMemory( &SearchExpression, sizeof( SearchExpression )); // // Otherwise build the CdName from the name in the stack location. // This involves building both the name and version portions and // checking for wild card characters. We also upcase the string if // this is a case-insensitive search. // } else { // // Create a CdName to check for wild cards. // WildCardName.FileName = *FileName; CdConvertNameToCdName( IrpContext, &WildCardName ); // // The name better have at least one character. // if (WildCardName.FileName.Length == 0) { CdRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER ); } // // Check for wildcards in the separate components. // if (FsRtlDoesNameContainWildCards( &WildCardName.FileName)) { SetFlag( CcbFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD ); } if ((WildCardName.VersionString.Length != 0) && (FsRtlDoesNameContainWildCards( &WildCardName.VersionString ))) { SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD ); // // Check if this is a wild card only and match all version // strings. // if ((WildCardName.VersionString.Length == sizeof( WCHAR )) && (WildCardName.VersionString.Buffer[0] == L'*')) { SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL ); } } // // Now create the search expression to store in the Ccb. // SearchExpression.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, FileName->Length, TAG_ENUM_EXPRESSION ); SearchExpression.FileName.MaximumLength = FileName->Length; // // Either copy the name directly or perform the upcase. // if (FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) { Status = RtlUpcaseUnicodeString( (PUNICODE_STRING) &SearchExpression.FileName, FileName, FALSE ); // // This should never fail. // ASSERT( Status == STATUS_SUCCESS ); } else { RtlCopyMemory( SearchExpression.FileName.Buffer, FileName->Buffer, FileName->Length ); } // // Now split into the separate name and version components. // SearchExpression.FileName.Length = WildCardName.FileName.Length; SearchExpression.VersionString.Length = WildCardName.VersionString.Length; SearchExpression.VersionString.MaximumLength = WildCardName.VersionString.MaximumLength; SearchExpression.VersionString.Buffer = Add2Ptr( SearchExpression.FileName.Buffer, SearchExpression.FileName.Length + sizeof( WCHAR ), PWCHAR ); } // // But we do not want to return the constant "." and ".." entries for // the root directory, for consistency with the rest of Microsoft's // filesystems. // if (Fcb == Fcb->Vcb->RootIndexFcb) { SetFlag( CcbFlags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY ); } // // Now lock the Fcb in order to update the Ccb with the inital // enumeration values. // CdLockFcb( IrpContext, Fcb ); // // Check again that this is the initial search. // if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) { // // Update the values in the Ccb. // Ccb->CurrentDirentOffset = Fcb->StreamOffset; Ccb->SearchExpression = SearchExpression; // // Set the appropriate flags in the Ccb. // SetFlag( Ccb->Flags, CcbFlags | CCB_FLAG_ENUM_INITIALIZED ); // // Otherwise cleanup any buffer allocated here. // } else { if (!FlagOn( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL )) { CdFreePool( &SearchExpression.FileName.Buffer ); } } // // Otherwise lock the Fcb so we can read the current enumeration values. // } else { CdLockFcb( IrpContext, Fcb ); } // // Capture the current state of the enumeration. // // If the user specified an index then use his offset. We always // return the next entry in this case. // if (FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )) { KnownOffset = FALSE; DirentOffset = IrpSp->Parameters.QueryDirectory.FileIndex; *ReturnNextEntry = TRUE; // // If we are restarting the scan then go from the self entry. // } else if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) { KnownOffset = TRUE; DirentOffset = Fcb->StreamOffset; *ReturnNextEntry = FALSE; // // Otherwise use the values from the Ccb. // } else { KnownOffset = TRUE; DirentOffset = Ccb->CurrentDirentOffset; *ReturnNextEntry = BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); } // // Unlock the Fcb. // CdUnlockFcb( IrpContext, Fcb ); // // We have the starting offset in the directory and whether to return // that entry or the next. If we are at the beginning of the directory // and are returning that entry, then tell our caller this is the // initial query. // *InitialQuery = FALSE; if ((DirentOffset == Fcb->StreamOffset) && !(*ReturnNextEntry)) { *InitialQuery = TRUE; } // // If there is no file object then create it now. // if (Fcb->FileObject == NULL) { CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); } // // Determine the offset in the stream to position the FileContext and // whether this offset is known to be a file offset. // // If this offset is known to be safe then go ahead and position the // file context. This handles the cases where the offset is the beginning // of the stream, the offset is from a previous search or this is the // initial query. // if (KnownOffset) { CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, DirentOffset ); // // Otherwise we walk through the directory from the beginning until // we reach the entry which contains this offset. // } else { LastDirentOffset = Fcb->StreamOffset; Found = TRUE; CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset ); // // If the requested offset is prior to the beginning offset in the stream // then don't return the next entry. // if (DirentOffset < LastDirentOffset) { *ReturnNextEntry = FALSE; // // Else look for the last entry which ends past the desired index. // } else { // // Keep walking through the directory until we run out of // entries or we find an entry which ends beyond the input // index value. // do { // // If we have passed the index value then exit. // if (FileContext->InitialDirent->Dirent.DirentOffset > DirentOffset) { Found = FALSE; break; } // // Remember the current position in case we need to go back. // LastDirentOffset = FileContext->InitialDirent->Dirent.DirentOffset; // // Exit if the next entry is beyond the desired index value. // if (LastDirentOffset + FileContext->InitialDirent->Dirent.DirentLength > DirentOffset) { break; } Found = CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ); } while (Found); // // If we didn't find the entry then go back to the last known entry. // This can happen if the index lies in the unused range at the // end of a sector. // if (!Found) { CdCleanupFileContext( IrpContext, FileContext ); CdInitializeFileContext( IrpContext, FileContext ); CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset ); } } } // // Only update the dirent name if we will need it for some reason. // Don't update this name if we are returning the next entry and // the search string has a version component. // FileContext->ShortName.FileName.Length = 0; if (!(*ReturnNextEntry) || (Ccb->SearchExpression.VersionString.Length == 0)) { // // Update the name in the dirent into filename and version components. // CdUpdateDirentName( IrpContext, &FileContext->InitialDirent->Dirent, FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )); } // // Look at the flag in the IrpSp indicating whether to return just // one entry. // *ReturnSingleEntry = FALSE; if (FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )) { *ReturnSingleEntry = TRUE; } return; }
NTSTATUS CdCommonQueryInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for query file information called by both the fsd and fsp threads. Arguments: Irp - Supplies the Irp to process. Return Value: NTSTATUS - The return status for this operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); ULONG Length; FILE_INFORMATION_CLASS FileInformationClass; PFILE_ALL_INFORMATION Buffer; TYPE_OF_OPEN TypeOfOpen; PFCB Fcb; PCCB Ccb; BOOLEAN ReleaseFcb = FALSE; PAGED_CODE(); // // Reference our input parameters to make things easier // Length = IrpSp->Parameters.QueryFile.Length; FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; Buffer = Irp->AssociatedIrp.SystemBuffer; // // Decode the file object // TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); // // Use a try-finally to facilitate cleanup. // try { // // We only support query on file and directory handles. // switch (TypeOfOpen) { case UserDirectoryOpen : case UserFileOpen : // // Acquire shared access to this file. NOTE that this could be // a recursive acquire, if we already preacquired in // CdAcquireForCreateSection(). // CdAcquireFileShared( IrpContext, Fcb ); ReleaseFcb = TRUE; // // Make sure we have the correct sizes for a directory. // if (!FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED )) { ASSERT( TypeOfOpen == UserDirectoryOpen ); CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); } // // Make sure the Fcb is in a usable condition. This will raise // an error condition if the volume is unusable // CdVerifyFcbOperation( IrpContext, Fcb ); // // Based on the information class we'll do different // actions. Each of hte procedures that we're calling fills // up the output buffer, if possible. They will raise the // status STATUS_BUFFER_OVERFLOW for an insufficient buffer. // This is considered a somewhat unusual case and is handled // more cleanly with the exception mechanism rather than // testing a return status value for each call. // switch (FileInformationClass) { case FileAllInformation: // // We don't allow this operation on a file opened by file Id. // if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_ID )) { Status = STATUS_INVALID_PARAMETER; break; } // // In this case go ahead and call the individual routines to // fill in the buffer. Only the name routine will // pointer to the output buffer and then call the // individual routines to fill in the buffer. // Length -= (sizeof( FILE_ACCESS_INFORMATION ) + sizeof( FILE_MODE_INFORMATION ) + sizeof( FILE_ALIGNMENT_INFORMATION )); CdQueryBasicInfo( IrpContext, Fcb, &Buffer->BasicInformation, &Length ); CdQueryStandardInfo( IrpContext, Fcb, &Buffer->StandardInformation, &Length ); CdQueryInternalInfo( IrpContext, Fcb, &Buffer->InternalInformation, &Length ); CdQueryEaInfo( IrpContext, Fcb, &Buffer->EaInformation, &Length ); CdQueryPositionInfo( IrpContext, IrpSp->FileObject, &Buffer->PositionInformation, &Length ); Status = CdQueryNameInfo( IrpContext, IrpSp->FileObject, &Buffer->NameInformation, &Length ); break; case FileBasicInformation: CdQueryBasicInfo( IrpContext, Fcb, (PFILE_BASIC_INFORMATION) Buffer, &Length ); break; case FileStandardInformation: CdQueryStandardInfo( IrpContext, Fcb, (PFILE_STANDARD_INFORMATION) Buffer, &Length ); break; case FileInternalInformation: CdQueryInternalInfo( IrpContext, Fcb, (PFILE_INTERNAL_INFORMATION) Buffer, &Length ); break; case FileEaInformation: CdQueryEaInfo( IrpContext, Fcb, (PFILE_EA_INFORMATION) Buffer, &Length ); break; case FilePositionInformation: CdQueryPositionInfo( IrpContext, IrpSp->FileObject, (PFILE_POSITION_INFORMATION) Buffer, &Length ); break; case FileNameInformation: // // We don't allow this operation on a file opened by file Id. // if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_ID )) { Status = CdQueryNameInfo( IrpContext, IrpSp->FileObject, (PFILE_NAME_INFORMATION) Buffer, &Length ); } else { Status = STATUS_INVALID_PARAMETER; } break; case FileAlternateNameInformation: if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_ID )) { Status = CdQueryAlternateNameInfo( IrpContext, Fcb, Ccb, (PFILE_NAME_INFORMATION) Buffer, &Length ); } else { Status = STATUS_INVALID_PARAMETER; } break; case FileNetworkOpenInformation: CdQueryNetworkInfo( IrpContext, Fcb, (PFILE_NETWORK_OPEN_INFORMATION) Buffer, &Length ); break; default : Status = STATUS_INVALID_PARAMETER; } break; default : Status = STATUS_INVALID_PARAMETER; } // // Set the information field to the number of bytes actually filled in // and then complete the request // Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length; } finally { // // Release the file. // if (ReleaseFcb) { CdReleaseFile( IrpContext, Fcb ); } } // // Complete the request if we didn't raise. // CdCompleteRequest( IrpContext, Irp, Status ); return Status; }