mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
812 lines
24 KiB
C
812 lines
24 KiB
C
/*
|
|
* ReactOS kernel
|
|
* Copyright (C) 2002, 2004 ReactOS Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: drivers/filesystems/cdfs/dirctl.c
|
|
* PURPOSE: CDROM (ISO 9660) filesystem driver
|
|
* PROGRAMMER: Art Yerkes
|
|
* Eric Kohl
|
|
* UPDATE HISTORY:
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "cdfs.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
#define ROUND_DOWN(N, S) (((N) / (S)) * (S))
|
|
#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
/*
|
|
* FUNCTION: Retrieves the file name, be it in short or long file name format
|
|
*/
|
|
static NTSTATUS
|
|
CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt,
|
|
PVOID *Context,
|
|
PVOID *Block,
|
|
PLARGE_INTEGER StreamOffset,
|
|
ULONG DirLength,
|
|
PVOID *Ptr,
|
|
PWSTR Name,
|
|
PULONG pIndex,
|
|
PULONG CurrentOffset)
|
|
{
|
|
PDIR_RECORD Record = *Ptr;
|
|
ULONG Index;
|
|
|
|
if (*CurrentOffset >= DirLength)
|
|
return(STATUS_NO_MORE_ENTRIES);
|
|
|
|
if (*CurrentOffset == 0)
|
|
{
|
|
Index = 0;
|
|
Record = (PDIR_RECORD)*Block;
|
|
while (Index < *pIndex)
|
|
{
|
|
(*Ptr) = (PVOID)((ULONG_PTR)(*Ptr) + Record->RecordLength);
|
|
(*CurrentOffset) += Record->RecordLength;
|
|
Record = *Ptr;
|
|
if ((ULONG_PTR)(*Ptr) - (ULONG_PTR)(*Block) >= BLOCKSIZE || Record->RecordLength == 0)
|
|
{
|
|
DPRINT("Map next sector\n");
|
|
CcUnpinData(*Context);
|
|
StreamOffset->QuadPart += BLOCKSIZE;
|
|
*CurrentOffset = ROUND_UP(*CurrentOffset, BLOCKSIZE);
|
|
_SEH2_TRY
|
|
{
|
|
CcMapData(DeviceExt->StreamFileObject, StreamOffset, BLOCKSIZE, MAP_WAIT, Context, Block);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DPRINT("CcMapData() failed\n");
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
*Ptr = *Block;
|
|
Record = (PDIR_RECORD)*Ptr;
|
|
}
|
|
if (*CurrentOffset >= DirLength)
|
|
return(STATUS_NO_MORE_ENTRIES);
|
|
|
|
Index++;
|
|
}
|
|
}
|
|
|
|
if ((ULONG_PTR)(*Ptr) - (ULONG_PTR)(*Block) >= BLOCKSIZE || Record->RecordLength == 0)
|
|
{
|
|
DPRINT("Map next sector\n");
|
|
CcUnpinData(*Context);
|
|
StreamOffset->QuadPart += BLOCKSIZE;
|
|
*CurrentOffset = ROUND_UP(*CurrentOffset, BLOCKSIZE);
|
|
_SEH2_TRY
|
|
{
|
|
CcMapData(DeviceExt->StreamFileObject, StreamOffset, BLOCKSIZE, MAP_WAIT, Context, Block);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DPRINT("CcMapData() failed\n");
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
*Ptr = *Block;
|
|
Record = (PDIR_RECORD)*Ptr;
|
|
}
|
|
|
|
if (*CurrentOffset >= DirLength)
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
|
|
DPRINT("Index %lu RecordLength %lu Offset %lu\n",
|
|
*pIndex, Record->RecordLength, *CurrentOffset);
|
|
|
|
if (!CdfsIsRecordValid(DeviceExt, Record))
|
|
{
|
|
CcUnpinData(*Context);
|
|
return STATUS_DISK_CORRUPT_ERROR;
|
|
}
|
|
|
|
CdfsGetDirEntryName(DeviceExt, Record, Name);
|
|
|
|
*Ptr = Record;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
/*
|
|
* FUNCTION: Find a file
|
|
*/
|
|
static NTSTATUS
|
|
CdfsFindFile(PDEVICE_EXTENSION DeviceExt,
|
|
PFCB Fcb,
|
|
PFCB Parent,
|
|
PUNICODE_STRING FileToFind,
|
|
PULONG pDirIndex,
|
|
PULONG pOffset)
|
|
{
|
|
WCHAR name[256];
|
|
WCHAR ShortNameBuffer[13];
|
|
UNICODE_STRING TempString;
|
|
UNICODE_STRING ShortName;
|
|
UNICODE_STRING LongName;
|
|
UNICODE_STRING FileToFindUpcase;
|
|
PVOID Block;
|
|
NTSTATUS Status;
|
|
ULONG len;
|
|
ULONG DirIndex;
|
|
ULONG Offset = 0;
|
|
BOOLEAN IsRoot;
|
|
PVOID Context = NULL;
|
|
ULONG DirSize;
|
|
PDIR_RECORD Record;
|
|
LARGE_INTEGER StreamOffset, OffsetOfEntry;
|
|
|
|
DPRINT("FindFile(Parent %p, FileToFind '%wZ', DirIndex: %u)\n",
|
|
Parent, FileToFind, pDirIndex ? *pDirIndex : 0);
|
|
DPRINT("FindFile: old Pathname %p, old Objectname %p)\n",
|
|
Fcb->PathName, Fcb->ObjectName);
|
|
|
|
IsRoot = FALSE;
|
|
DirIndex = 0;
|
|
|
|
if (FileToFind == NULL || FileToFind->Length == 0)
|
|
{
|
|
RtlInitUnicodeString(&TempString, L".");
|
|
FileToFind = &TempString;
|
|
}
|
|
|
|
if (Parent)
|
|
{
|
|
if (Parent->Entry.ExtentLocationL == DeviceExt->CdInfo.RootStart)
|
|
{
|
|
IsRoot = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IsRoot = TRUE;
|
|
}
|
|
|
|
if (IsRoot != FALSE)
|
|
{
|
|
StreamOffset.QuadPart = (LONGLONG)DeviceExt->CdInfo.RootStart * (LONGLONG)BLOCKSIZE;
|
|
DirSize = DeviceExt->CdInfo.RootSize;
|
|
|
|
|
|
if (FileToFind->Buffer[0] == 0 ||
|
|
(FileToFind->Buffer[0] == '\\' && FileToFind->Buffer[1] == 0) ||
|
|
(FileToFind->Buffer[0] == '.' && FileToFind->Buffer[1] == 0))
|
|
{
|
|
/* it's root : complete essentials fields then return ok */
|
|
RtlZeroMemory(Fcb, sizeof(FCB));
|
|
RtlInitEmptyUnicodeString(&Fcb->PathName, Fcb->PathNameBuffer, sizeof(Fcb->PathNameBuffer));
|
|
|
|
Fcb->PathNameBuffer[0] = '\\';
|
|
Fcb->PathName.Length = sizeof(WCHAR);
|
|
Fcb->ObjectName = &Fcb->PathNameBuffer[1];
|
|
Fcb->Entry.ExtentLocationL = DeviceExt->CdInfo.RootStart;
|
|
Fcb->Entry.DataLengthL = DeviceExt->CdInfo.RootSize;
|
|
Fcb->Entry.FileFlags = 0x02; //FILE_ATTRIBUTE_DIRECTORY;
|
|
|
|
if (pDirIndex)
|
|
*pDirIndex = 0;
|
|
if (pOffset)
|
|
*pOffset = 0;
|
|
DPRINT("CdfsFindFile: new Pathname %wZ, new Objectname %S)\n",&Fcb->PathName, Fcb->ObjectName);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
StreamOffset.QuadPart = (LONGLONG)Parent->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
|
|
DirSize = Parent->Entry.DataLengthL;
|
|
}
|
|
|
|
DPRINT("StreamOffset %I64d DirSize %u\n", StreamOffset.QuadPart, DirSize);
|
|
|
|
if (pDirIndex && (*pDirIndex))
|
|
DirIndex = *pDirIndex;
|
|
|
|
if (pOffset && (*pOffset))
|
|
{
|
|
Offset = *pOffset;
|
|
StreamOffset.QuadPart += ROUND_DOWN(Offset, BLOCKSIZE);
|
|
}
|
|
|
|
_SEH2_TRY
|
|
{
|
|
CcMapData(DeviceExt->StreamFileObject, &StreamOffset, BLOCKSIZE, MAP_WAIT, &Context, &Block);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DPRINT("CcMapData() failed\n");
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
Record = (PDIR_RECORD) ((ULONG_PTR)Block + Offset % BLOCKSIZE);
|
|
if (Offset)
|
|
{
|
|
Offset += Record->RecordLength;
|
|
Record = (PDIR_RECORD)((ULONG_PTR)Record + Record->RecordLength);
|
|
}
|
|
|
|
/* Upper case the expression for FsRtlIsNameInExpression */
|
|
Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFind, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
while(TRUE)
|
|
{
|
|
DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
|
|
Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
|
|
|
|
Status = CdfsGetEntryName
|
|
(DeviceExt, &Context, &Block, &StreamOffset,
|
|
DirSize, (PVOID*)&Record, name, &DirIndex, &Offset);
|
|
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
{
|
|
break;
|
|
}
|
|
else if (Status == STATUS_UNSUCCESSFUL || Status == STATUS_DISK_CORRUPT_ERROR)
|
|
{
|
|
/* Note: the directory cache has already been unpinned */
|
|
RtlFreeUnicodeString(&FileToFindUpcase);
|
|
return Status;
|
|
}
|
|
|
|
DPRINT("Name '%S'\n", name);
|
|
|
|
RtlInitUnicodeString(&LongName, name);
|
|
|
|
ShortName.Length = 0;
|
|
ShortName.MaximumLength = 26;
|
|
ShortName.Buffer = ShortNameBuffer;
|
|
|
|
OffsetOfEntry.QuadPart = StreamOffset.QuadPart + Offset;
|
|
CdfsShortNameCacheGet(Parent, &OffsetOfEntry, &LongName, &ShortName);
|
|
|
|
DPRINT("ShortName '%wZ'\n", &ShortName);
|
|
|
|
if (FsRtlIsNameInExpression(&FileToFindUpcase, &LongName, TRUE, NULL) ||
|
|
FsRtlIsNameInExpression(&FileToFindUpcase, &ShortName, TRUE, NULL))
|
|
{
|
|
if (Parent->PathName.Buffer[0])
|
|
{
|
|
RtlCopyUnicodeString(&Fcb->PathName, &Parent->PathName);
|
|
len = Parent->PathName.Length / sizeof(WCHAR);
|
|
Fcb->ObjectName=&Fcb->PathName.Buffer[len];
|
|
if (len != 1 || Fcb->PathName.Buffer[0] != '\\')
|
|
{
|
|
Fcb->ObjectName[0] = '\\';
|
|
Fcb->ObjectName = &Fcb->ObjectName[1];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fcb->ObjectName=Fcb->PathName.Buffer;
|
|
Fcb->ObjectName[0]='\\';
|
|
Fcb->ObjectName=&Fcb->ObjectName[1];
|
|
}
|
|
|
|
DPRINT("PathName '%wZ' ObjectName '%S'\n", &Fcb->PathName, Fcb->ObjectName);
|
|
|
|
memcpy(&Fcb->Entry, Record, sizeof(DIR_RECORD));
|
|
wcsncpy(Fcb->ObjectName, name, min(wcslen(name) + 1,
|
|
MAX_PATH - (Fcb->PathName.Length / sizeof(WCHAR)) + wcslen(Fcb->ObjectName)));
|
|
|
|
/* Copy short name */
|
|
Fcb->ShortNameU.Length = ShortName.Length;
|
|
Fcb->ShortNameU.MaximumLength = ShortName.Length;
|
|
Fcb->ShortNameU.Buffer = Fcb->ShortNameBuffer;
|
|
memcpy(Fcb->ShortNameBuffer, ShortName.Buffer, ShortName.Length);
|
|
|
|
if (pDirIndex)
|
|
*pDirIndex = DirIndex;
|
|
if (pOffset)
|
|
*pOffset = Offset;
|
|
|
|
DPRINT("FindFile: new Pathname %wZ, new Objectname %S, DirIndex %u\n",
|
|
&Fcb->PathName, Fcb->ObjectName, DirIndex);
|
|
|
|
RtlFreeUnicodeString(&FileToFindUpcase);
|
|
CcUnpinData(Context);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Offset += Record->RecordLength;
|
|
Record = (PDIR_RECORD)((ULONG_PTR)Record + Record->RecordLength);
|
|
DirIndex++;
|
|
}
|
|
|
|
RtlFreeUnicodeString(&FileToFindUpcase);
|
|
CcUnpinData(Context);
|
|
|
|
if (pDirIndex)
|
|
*pDirIndex = DirIndex;
|
|
|
|
if (pOffset)
|
|
*pOffset = Offset;
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
CdfsGetNameInformation(PFCB Fcb,
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PFILE_NAMES_INFORMATION Info,
|
|
ULONG BufferLength)
|
|
{
|
|
ULONG Length;
|
|
|
|
DPRINT("CdfsGetNameInformation() called\n");
|
|
|
|
UNREFERENCED_PARAMETER(DeviceExt);
|
|
|
|
Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
|
|
if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength)
|
|
return(STATUS_BUFFER_OVERFLOW);
|
|
|
|
Info->FileNameLength = Length;
|
|
Info->NextEntryOffset =
|
|
ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG));
|
|
RtlCopyMemory(Info->FileName, Fcb->ObjectName, Length);
|
|
|
|
// Info->FileIndex=;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
CdfsGetDirectoryInformation(PFCB Fcb,
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PFILE_DIRECTORY_INFORMATION Info,
|
|
ULONG BufferLength)
|
|
{
|
|
ULONG Length;
|
|
|
|
DPRINT("CdfsGetDirectoryInformation() called\n");
|
|
|
|
UNREFERENCED_PARAMETER(DeviceExt);
|
|
|
|
Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
|
|
if ((sizeof (FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
|
|
return(STATUS_BUFFER_OVERFLOW);
|
|
|
|
Info->FileNameLength = Length;
|
|
Info->NextEntryOffset =
|
|
ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
|
|
RtlCopyMemory(Info->FileName, Fcb->ObjectName, Length);
|
|
|
|
/* Convert file times */
|
|
CdfsDateTimeToSystemTime(Fcb,
|
|
&Info->CreationTime);
|
|
Info->LastWriteTime = Info->CreationTime;
|
|
Info->ChangeTime = Info->CreationTime;
|
|
|
|
/* Convert file flags */
|
|
CdfsFileFlagsToAttributes(Fcb,
|
|
&Info->FileAttributes);
|
|
if (CdfsFCBIsDirectory(Fcb))
|
|
{
|
|
Info->EndOfFile.QuadPart = 0;
|
|
Info->AllocationSize.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
|
|
|
|
/* Make AllocSize a rounded up multiple of the sector size */
|
|
Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
|
|
}
|
|
|
|
// Info->FileIndex=;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
CdfsGetFullDirectoryInformation(PFCB Fcb,
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PFILE_FULL_DIR_INFORMATION Info,
|
|
ULONG BufferLength)
|
|
{
|
|
ULONG Length;
|
|
|
|
DPRINT("CdfsGetFullDirectoryInformation() called\n");
|
|
|
|
UNREFERENCED_PARAMETER(DeviceExt);
|
|
|
|
Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
|
|
if ((sizeof (FILE_FULL_DIR_INFORMATION) + Length) > BufferLength)
|
|
return(STATUS_BUFFER_OVERFLOW);
|
|
|
|
Info->FileNameLength = Length;
|
|
Info->NextEntryOffset =
|
|
ROUND_UP(sizeof(FILE_FULL_DIR_INFORMATION) + Length, sizeof(ULONG));
|
|
RtlCopyMemory(Info->FileName, Fcb->ObjectName, Length);
|
|
|
|
/* Convert file times */
|
|
CdfsDateTimeToSystemTime(Fcb,
|
|
&Info->CreationTime);
|
|
Info->LastWriteTime = Info->CreationTime;
|
|
Info->ChangeTime = Info->CreationTime;
|
|
|
|
/* Convert file flags */
|
|
CdfsFileFlagsToAttributes(Fcb,
|
|
&Info->FileAttributes);
|
|
|
|
if (CdfsFCBIsDirectory(Fcb))
|
|
{
|
|
Info->EndOfFile.QuadPart = 0;
|
|
Info->AllocationSize.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
|
|
|
|
/* Make AllocSize a rounded up multiple of the sector size */
|
|
Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
|
|
}
|
|
|
|
// Info->FileIndex=;
|
|
Info->EaSize = 0;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
CdfsGetBothDirectoryInformation(PFCB Fcb,
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PFILE_BOTH_DIR_INFORMATION Info,
|
|
ULONG BufferLength)
|
|
{
|
|
ULONG Length;
|
|
|
|
DPRINT("CdfsGetBothDirectoryInformation() called\n");
|
|
|
|
UNREFERENCED_PARAMETER(DeviceExt);
|
|
|
|
Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
|
|
if ((sizeof (FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
|
|
return(STATUS_BUFFER_OVERFLOW);
|
|
|
|
Info->FileNameLength = Length;
|
|
Info->NextEntryOffset =
|
|
ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
|
|
RtlCopyMemory(Info->FileName, Fcb->ObjectName, Length);
|
|
|
|
/* Convert file times */
|
|
CdfsDateTimeToSystemTime(Fcb,
|
|
&Info->CreationTime);
|
|
Info->LastWriteTime = Info->CreationTime;
|
|
Info->ChangeTime = Info->CreationTime;
|
|
|
|
/* Convert file flags */
|
|
CdfsFileFlagsToAttributes(Fcb,
|
|
&Info->FileAttributes);
|
|
|
|
if (CdfsFCBIsDirectory(Fcb))
|
|
{
|
|
Info->EndOfFile.QuadPart = 0;
|
|
Info->AllocationSize.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
|
|
|
|
/* Make AllocSize a rounded up multiple of the sector size */
|
|
Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
|
|
}
|
|
|
|
// Info->FileIndex=;
|
|
Info->EaSize = 0;
|
|
|
|
/* Copy short name */
|
|
ASSERT(Fcb->ShortNameU.Length / sizeof(WCHAR) <= 12);
|
|
Info->ShortNameLength = (CCHAR)Fcb->ShortNameU.Length;
|
|
RtlCopyMemory(Info->ShortName, Fcb->ShortNameU.Buffer, Fcb->ShortNameU.Length);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
CdfsQueryDirectory(PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
LONG BufferLength = 0;
|
|
PUNICODE_STRING SearchPattern = NULL;
|
|
FILE_INFORMATION_CLASS FileInformationClass;
|
|
ULONG FileIndex = 0;
|
|
PUCHAR Buffer = NULL;
|
|
PFILE_NAMES_INFORMATION Buffer0 = NULL;
|
|
PFCB Fcb;
|
|
PCCB Ccb;
|
|
FCB TempFcb;
|
|
BOOLEAN First = FALSE;
|
|
PIO_STACK_LOCATION Stack;
|
|
PFILE_OBJECT FileObject;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
DPRINT("CdfsQueryDirectory() called\n");
|
|
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
FileObject = Stack->FileObject;
|
|
RtlInitEmptyUnicodeString(&TempFcb.PathName, TempFcb.PathNameBuffer, sizeof(TempFcb.PathNameBuffer));
|
|
|
|
Ccb = (PCCB)FileObject->FsContext2;
|
|
Fcb = (PFCB)FileObject->FsContext;
|
|
|
|
/* Obtain the callers parameters */
|
|
BufferLength = Stack->Parameters.QueryDirectory.Length;
|
|
SearchPattern = Stack->Parameters.QueryDirectory.FileName;
|
|
FileInformationClass =
|
|
Stack->Parameters.QueryDirectory.FileInformationClass;
|
|
FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
|
|
|
|
/* Determine Buffer for result */
|
|
if (Irp->MdlAddress)
|
|
{
|
|
Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
|
|
}
|
|
else
|
|
{
|
|
Buffer = Irp->UserBuffer;
|
|
}
|
|
|
|
/* Allocate search pattern in case:
|
|
* -> We don't have one already in context
|
|
* -> We have been given an input pattern
|
|
* -> The pattern length is not null
|
|
* -> The pattern buffer is not null
|
|
* Otherwise, we'll fall later and allocate a match all (*) pattern
|
|
*/
|
|
if (SearchPattern != NULL &&
|
|
SearchPattern->Length != 0 && SearchPattern->Buffer != NULL)
|
|
{
|
|
if (Ccb->DirectorySearchPattern.Buffer == NULL)
|
|
{
|
|
First = TRUE;
|
|
Ccb->DirectorySearchPattern.Buffer =
|
|
ExAllocatePoolWithTag(NonPagedPool, SearchPattern->Length + sizeof(WCHAR), CDFS_SEARCH_PATTERN_TAG);
|
|
if (Ccb->DirectorySearchPattern.Buffer == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
Ccb->DirectorySearchPattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
|
|
RtlCopyUnicodeString(&Ccb->DirectorySearchPattern, SearchPattern);
|
|
Ccb->DirectorySearchPattern.Buffer[SearchPattern->Length / sizeof(WCHAR)] = 0;
|
|
}
|
|
}
|
|
else if (Ccb->DirectorySearchPattern.Buffer == NULL)
|
|
{
|
|
First = TRUE;
|
|
Ccb->DirectorySearchPattern.Buffer = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), CDFS_SEARCH_PATTERN_TAG);
|
|
if (Ccb->DirectorySearchPattern.Buffer == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Ccb->DirectorySearchPattern.Length = sizeof(WCHAR);
|
|
Ccb->DirectorySearchPattern.MaximumLength = 2 * sizeof(WCHAR);
|
|
Ccb->DirectorySearchPattern.Buffer[0] = L'*';
|
|
Ccb->DirectorySearchPattern.Buffer[1] = 0;
|
|
}
|
|
DPRINT("Search pattern '%wZ'\n", &Ccb->DirectorySearchPattern);
|
|
|
|
/* Determine directory index */
|
|
if (Stack->Flags & SL_INDEX_SPECIFIED)
|
|
{
|
|
Ccb->Entry = Stack->Parameters.QueryDirectory.FileIndex;
|
|
Ccb->Offset = Ccb->CurrentByteOffset.u.LowPart;
|
|
}
|
|
else if (First || (Stack->Flags & SL_RESTART_SCAN))
|
|
{
|
|
Ccb->Entry = 0;
|
|
Ccb->Offset = 0;
|
|
}
|
|
DPRINT("Buffer = %p tofind = %wZ\n", Buffer, &Ccb->DirectorySearchPattern);
|
|
|
|
TempFcb.ObjectName = TempFcb.PathName.Buffer;
|
|
while (Status == STATUS_SUCCESS && BufferLength > 0)
|
|
{
|
|
Status = CdfsFindFile(DeviceExtension,
|
|
&TempFcb,
|
|
Fcb,
|
|
&Ccb->DirectorySearchPattern,
|
|
&Ccb->Entry,
|
|
&Ccb->Offset);
|
|
DPRINT("Found %S, Status=%x, entry %x\n", TempFcb.ObjectName, Status, Ccb->Entry);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
switch (FileInformationClass)
|
|
{
|
|
case FileNameInformation:
|
|
Status = CdfsGetNameInformation(&TempFcb,
|
|
DeviceExtension,
|
|
(PFILE_NAMES_INFORMATION)Buffer,
|
|
BufferLength);
|
|
break;
|
|
|
|
case FileDirectoryInformation:
|
|
Status = CdfsGetDirectoryInformation(&TempFcb,
|
|
DeviceExtension,
|
|
(PFILE_DIRECTORY_INFORMATION)Buffer,
|
|
BufferLength);
|
|
break;
|
|
|
|
case FileFullDirectoryInformation:
|
|
Status = CdfsGetFullDirectoryInformation(&TempFcb,
|
|
DeviceExtension,
|
|
(PFILE_FULL_DIR_INFORMATION)Buffer,
|
|
BufferLength);
|
|
break;
|
|
|
|
case FileBothDirectoryInformation:
|
|
Status = CdfsGetBothDirectoryInformation(&TempFcb,
|
|
DeviceExtension,
|
|
(PFILE_BOTH_DIR_INFORMATION)Buffer,
|
|
BufferLength);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
if (Status == STATUS_BUFFER_OVERFLOW)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (First)
|
|
{
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_MORE_FILES;
|
|
}
|
|
break;
|
|
}
|
|
|
|
Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
|
|
Buffer0->FileIndex = FileIndex++;
|
|
Ccb->Entry++;
|
|
BufferLength -= Buffer0->NextEntryOffset;
|
|
|
|
if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Buffer += Buffer0->NextEntryOffset;
|
|
}
|
|
|
|
if (Buffer0)
|
|
{
|
|
Buffer0->NextEntryOffset = 0;
|
|
}
|
|
|
|
if (FileIndex > 0)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = Stack->Parameters.QueryDirectory.Length - BufferLength;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
CdfsNotifyChangeDirectory(PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PCDFS_IRP_CONTEXT IrpContext)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
PFCB Fcb;
|
|
PCCB Ccb;
|
|
PIO_STACK_LOCATION Stack;
|
|
PFILE_OBJECT FileObject;
|
|
|
|
DPRINT("CdfsNotifyChangeDirectory() called\n");
|
|
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
FileObject = Stack->FileObject;
|
|
|
|
Ccb = (PCCB)FileObject->FsContext2;
|
|
Fcb = (PFCB)FileObject->FsContext;
|
|
|
|
FsRtlNotifyFullChangeDirectory(DeviceExtension->NotifySync,
|
|
&(DeviceExtension->NotifyList),
|
|
Ccb,
|
|
(PSTRING)&(Fcb->PathName),
|
|
BooleanFlagOn(Stack->Flags, SL_WATCH_TREE),
|
|
FALSE,
|
|
Stack->Parameters.NotifyDirectory.CompletionFilter,
|
|
Irp,
|
|
NULL,
|
|
NULL);
|
|
|
|
/* We won't handle IRP completion */
|
|
IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
CdfsDirectoryControl(
|
|
PCDFS_IRP_CONTEXT IrpContext)
|
|
{
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("CdfsDirectoryControl() called\n");
|
|
|
|
ASSERT(IrpContext);
|
|
|
|
Irp = IrpContext->Irp;
|
|
DeviceObject = IrpContext->DeviceObject;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
switch (IrpContext->MinorFunction)
|
|
{
|
|
case IRP_MN_QUERY_DIRECTORY:
|
|
Status = CdfsQueryDirectory(DeviceObject,
|
|
Irp);
|
|
break;
|
|
|
|
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
|
|
Status = CdfsNotifyChangeDirectory(DeviceObject,
|
|
Irp, IrpContext);
|
|
break;
|
|
|
|
default:
|
|
DPRINT1("CDFS: MinorFunction %u\n", IrpContext->MinorFunction);
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
/* EOF */
|