reactos/drivers/filesystems/ntfs/dirctl.c

623 lines
21 KiB
C
Raw Normal View History

/*
* ReactOS kernel
* Copyright (C) 2002, 2003, 2014 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/filesystem/ntfs/dirctl.c
* PURPOSE: NTFS filesystem driver
* PROGRAMMERS: Eric Kohl
* Pierre Schweitzer (pierre@reactos.org)
* Hervé Poussineau (hpoussin@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include "ntfs.h"
#define NDEBUG
#include <debug.h>
/* FUNCTIONS ****************************************************************/
ULONGLONG
NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
PFILE_RECORD_HEADER FileRecord,
PCWSTR Stream,
ULONG StreamLength,
PULONGLONG AllocatedSize)
{
ULONGLONG Size = 0ULL;
ULONGLONG Allocated = 0ULL;
NTSTATUS Status;
PNTFS_ATTR_CONTEXT DataContext;
Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL);
if (NT_SUCCESS(Status))
{
Size = AttributeDataLength(DataContext->pRecord);
Allocated = AttributeAllocatedLength(DataContext->pRecord);
ReleaseAttributeContext(DataContext);
}
if (AllocatedSize != NULL) *AllocatedSize = Allocated;
return Size;
}
#define ULONG_ROUND_UP(x) ROUND_UP((x), (sizeof(ULONG)))
static NTSTATUS
NtfsGetNamesInformation(PDEVICE_EXTENSION DeviceExt,
PFILE_RECORD_HEADER FileRecord,
ULONGLONG MFTIndex,
PFILE_NAMES_INFORMATION Info,
ULONG BufferLength,
PULONG Written,
BOOLEAN First)
{
ULONG Length;
NTSTATUS Status;
ULONG BytesToCopy = 0;
PFILENAME_ATTRIBUTE FileName;
DPRINT("NtfsGetNamesInformation() called\n");
*Written = 0;
Status = STATUS_BUFFER_OVERFLOW;
if (FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) > BufferLength)
{
return Status;
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
if (FileName == NULL)
{
DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
NtfsDumpFileAttributes(DeviceExt, FileRecord);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
Length = FileName->NameLength * sizeof (WCHAR);
if (First || (BufferLength >= FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) + Length))
{
Info->FileNameLength = Length;
*Written = FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName);
Info->NextEntryOffset = 0;
if (BufferLength > FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName))
{
BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName));
RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
*Written += BytesToCopy;
if (BytesToCopy == Length)
{
Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_NAMES_INFORMATION) +
BytesToCopy);
Status = STATUS_SUCCESS;
}
}
}
return Status;
}
static NTSTATUS
NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
PFILE_RECORD_HEADER FileRecord,
ULONGLONG MFTIndex,
PFILE_DIRECTORY_INFORMATION Info,
ULONG BufferLength,
PULONG Written,
BOOLEAN First)
{
ULONG Length;
NTSTATUS Status;
ULONG BytesToCopy = 0;
PFILENAME_ATTRIBUTE FileName;
PSTANDARD_INFORMATION StdInfo;
DPRINT("NtfsGetDirectoryInformation() called\n");
*Written = 0;
Status = STATUS_BUFFER_OVERFLOW;
if (FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) > BufferLength)
{
return Status;
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
if (FileName == NULL)
{
DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
NtfsDumpFileAttributes(DeviceExt, FileRecord);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
ASSERT(StdInfo != NULL);
Length = FileName->NameLength * sizeof (WCHAR);
if (First || (BufferLength >= FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) + Length))
{
Info->FileNameLength = Length;
*Written = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName);
Info->NextEntryOffset = 0;
if (BufferLength > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName))
{
BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName));
RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
*Written += BytesToCopy;
if (BytesToCopy == Length)
{
Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) +
BytesToCopy);
Status = STATUS_SUCCESS;
}
}
Info->CreationTime.QuadPart = FileName->CreationTime;
Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
Info->ChangeTime.QuadPart = FileName->ChangeTime;
/* Convert file flags */
NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
Info->FileIndex = MFTIndex;
}
return Status;
}
static NTSTATUS
NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
PFILE_RECORD_HEADER FileRecord,
ULONGLONG MFTIndex,
PFILE_FULL_DIRECTORY_INFORMATION Info,
ULONG BufferLength,
PULONG Written,
BOOLEAN First)
{
ULONG Length;
NTSTATUS Status;
ULONG BytesToCopy = 0;
PFILENAME_ATTRIBUTE FileName;
PSTANDARD_INFORMATION StdInfo;
DPRINT("NtfsGetFullDirectoryInformation() called\n");
*Written = 0;
Status = STATUS_BUFFER_OVERFLOW;
if (FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) > BufferLength)
{
return Status;
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
if (FileName == NULL)
{
DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
NtfsDumpFileAttributes(DeviceExt, FileRecord);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
ASSERT(StdInfo != NULL);
Length = FileName->NameLength * sizeof (WCHAR);
if (First || (BufferLength >= FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) + Length))
{
Info->FileNameLength = Length;
*Written = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName);
Info->NextEntryOffset = 0;
if (BufferLength > FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName))
{
BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName));
RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
*Written += BytesToCopy;
if (BytesToCopy == Length)
{
Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_FULL_DIR_INFORMATION) +
BytesToCopy);
Status = STATUS_SUCCESS;
}
}
Info->CreationTime.QuadPart = FileName->CreationTime;
Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
Info->ChangeTime.QuadPart = FileName->ChangeTime;
/* Convert file flags */
NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
Info->FileIndex = MFTIndex;
Info->EaSize = 0;
}
return Status;
}
static NTSTATUS
NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
PFILE_RECORD_HEADER FileRecord,
ULONGLONG MFTIndex,
PFILE_BOTH_DIR_INFORMATION Info,
ULONG BufferLength,
PULONG Written,
BOOLEAN First)
{
ULONG Length;
NTSTATUS Status;
ULONG BytesToCopy = 0;
PFILENAME_ATTRIBUTE FileName, ShortFileName;
PSTANDARD_INFORMATION StdInfo;
DPRINT("NtfsGetBothDirectoryInformation() called\n");
*Written = 0;
Status = STATUS_BUFFER_OVERFLOW;
if (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) > BufferLength)
{
return Status;
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
if (FileName == NULL)
{
DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
NtfsDumpFileAttributes(DeviceExt, FileRecord);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
ShortFileName = GetFileNameFromRecord(DeviceExt, FileRecord, NTFS_FILE_NAME_DOS);
[NTFS] Totally rewrite the way MFT records attributes are handled. Up to now, we were having really similar loops, only looking at the resident part of the attribute list, not really caring about how the loop was going. This was leading to some issues: - In case the attribute we were looking for was stored in the non-resident part of the attribute list, we would miss it (excepted in the case of FindAttribute() which was properly browsing the whole attribute list). - In the specific case of FindAttribute(), one would have been able to setup a broken MFT record with the resident attribute list pointing on the non resident attribute list which itself would point to the resident attribute list. In such case, the driver would loop forever caught on the loop, allocating tones of memory. It was possible to trigger this by user space, from a non-privileged user, just by browsing the right directory entry. - In the case of the other loops (non FindAttribute()), another issue (other than missing attributes) was present, one would have been able to setup a broken MFT record with an attribute of null-length. This would have caused the driver to loop forever on the attribute list. This could be triggered from usermode too. And could be triggered by a non-privileged user. This commit introduces a new set of functions for attributes browsing: FindFirstAttribute(), FindNextAttribute(), FindCloseAttribute(). It allows safely browsing attributes and handles broken cases. It also performs reading of the attribute list when present and makes sure there's only one read. This method should be the only one to use to browse the attributes. The whole NTFS code base has been converted to use this newly set of functions. This really simplifies the implementation of FindAttribute(), and prevent unsafe code duplication. CORE-10037 #resolve #comment Fixed with r68829 svn path=/trunk/; revision=68829
2015-08-26 18:20:04 +00:00
StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
ASSERT(StdInfo != NULL);
Length = FileName->NameLength * sizeof (WCHAR);
if (First || (BufferLength >= FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) + Length))
{
Info->FileNameLength = Length;
*Written = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName);
Info->NextEntryOffset = 0;
if (BufferLength > FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName))
{
BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName));
RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
*Written += BytesToCopy;
if (BytesToCopy == Length)
{
Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) +
BytesToCopy);
Status = STATUS_SUCCESS;
}
}
if (ShortFileName)
{
/* Should we upcase the filename? */
ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
}
else
{
Info->ShortName[0] = 0;
Info->ShortNameLength = 0;
}
Info->CreationTime.QuadPart = FileName->CreationTime;
Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
Info->ChangeTime.QuadPart = FileName->ChangeTime;
/* Convert file flags */
NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
Info->FileIndex = MFTIndex;
Info->EaSize = 0;
}
return Status;
}
NTSTATUS
NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
{
PIRP Irp;
PDEVICE_OBJECT DeviceObject;
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;
PNTFS_FCB Fcb;
PNTFS_CCB Ccb;
BOOLEAN First = FALSE;
PIO_STACK_LOCATION Stack;
PFILE_OBJECT FileObject;
NTSTATUS Status = STATUS_SUCCESS;
PFILE_RECORD_HEADER FileRecord;
ULONGLONG MFTRecord, OldMFTRecord = 0;
UNICODE_STRING Pattern;
ULONG Written;
DPRINT("NtfsQueryDirectory() called\n");
ASSERT(IrpContext);
Irp = IrpContext->Irp;
DeviceObject = IrpContext->DeviceObject;
DeviceExtension = DeviceObject->DeviceExtension;
Stack = IoGetCurrentIrpStackLocation(Irp);
FileObject = Stack->FileObject;
Ccb = (PNTFS_CCB)FileObject->FsContext2;
Fcb = (PNTFS_FCB)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;
if (NtfsFCBIsCompressed(Fcb))
{
DPRINT1("Compressed directory!\n");
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
{
return STATUS_PENDING;
}
if (SearchPattern != NULL)
{
if (!Ccb->DirectorySearchPattern)
{
First = TRUE;
Pattern.Length = 0;
Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
Ccb->DirectorySearchPattern = Pattern.Buffer =
ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
if (!Ccb->DirectorySearchPattern)
{
ExReleaseResourceLite(&Fcb->MainResource);
return STATUS_INSUFFICIENT_RESOURCES;
}
memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
}
}
else if (!Ccb->DirectorySearchPattern)
{
First = TRUE;
Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
if (!Ccb->DirectorySearchPattern)
{
ExReleaseResourceLite(&Fcb->MainResource);
return STATUS_INSUFFICIENT_RESOURCES;
}
Ccb->DirectorySearchPattern[0] = L'*';
Ccb->DirectorySearchPattern[1] = 0;
}
RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
DPRINT("In: '%S'\n", Fcb->PathName);
/* Determine directory index */
if (Stack->Flags & SL_INDEX_SPECIFIED)
{
Ccb->Entry = FileIndex;
}
else if (First || (Stack->Flags & SL_RESTART_SCAN))
{
Ccb->Entry = 0;
}
/* Get Buffer for result */
Buffer = NtfsGetUserBuffer(Irp, FALSE);
DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
if (!ExAcquireResourceExclusiveLite(&DeviceExtension->DirResource,
BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
{
ExReleaseResourceLite(&Fcb->MainResource);
return STATUS_PENDING;
}
Written = 0;
while (Status == STATUS_SUCCESS && BufferLength > 0)
{
Status = NtfsFindFileAt(DeviceExtension,
&Pattern,
&Ccb->Entry,
&FileRecord,
&MFTRecord,
Fcb->MFTIndex,
BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE));
if (NT_SUCCESS(Status))
{
/* HACK: files with both a short name and a long name are present twice in the index.
* Ignore the second entry, if it is immediately following the first one.
*/
if (MFTRecord == OldMFTRecord)
{
DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
Ccb->Entry++;
ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord);
continue;
}
OldMFTRecord = MFTRecord;
switch (FileInformationClass)
{
case FileNamesInformation:
Status = NtfsGetNamesInformation(DeviceExtension,
FileRecord,
MFTRecord,
(PFILE_NAMES_INFORMATION)Buffer,
BufferLength,
&Written,
Buffer0 == NULL);
break;
case FileDirectoryInformation:
Status = NtfsGetDirectoryInformation(DeviceExtension,
FileRecord,
MFTRecord,
(PFILE_DIRECTORY_INFORMATION)Buffer,
BufferLength,
&Written,
Buffer0 == NULL);
break;
case FileFullDirectoryInformation:
Status = NtfsGetFullDirectoryInformation(DeviceExtension,
FileRecord,
MFTRecord,
(PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
BufferLength,
&Written,
Buffer0 == NULL);
break;
case FileBothDirectoryInformation:
Status = NtfsGetBothDirectoryInformation(DeviceExtension,
FileRecord,
MFTRecord,
(PFILE_BOTH_DIR_INFORMATION)Buffer,
BufferLength,
&Written,
Buffer0 == NULL);
break;
default:
Status = STATUS_INVALID_INFO_CLASS;
}
if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_INVALID_INFO_CLASS)
{
break;
}
}
else
{
Status = (First ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES);
break;
}
Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
Buffer0->FileIndex = FileIndex++;
Ccb->Entry++;
BufferLength -= Buffer0->NextEntryOffset;
ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord);
if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
{
break;
}
Buffer += Buffer0->NextEntryOffset;
}
if (Buffer0)
{
Buffer0->NextEntryOffset = 0;
Status = STATUS_SUCCESS;
IrpContext->Irp->IoStatus.Information = Stack->Parameters.QueryDirectory.Length - BufferLength;
}
else
{
ASSERT(Status != STATUS_SUCCESS || BufferLength == 0);
ASSERT(Written <= Stack->Parameters.QueryDirectory.Length);
IrpContext->Irp->IoStatus.Information = Written;
}
ExReleaseResourceLite(&DeviceExtension->DirResource);
ExReleaseResourceLite(&Fcb->MainResource);
return Status;
}
NTSTATUS
NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
DPRINT("NtfsDirectoryControl() called\n");
switch (IrpContext->MinorFunction)
{
case IRP_MN_QUERY_DIRECTORY:
Status = NtfsQueryDirectory(IrpContext);
break;
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
default:
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
if (Status == STATUS_PENDING && IrpContext->Flags & IRPCONTEXT_COMPLETE)
{
return NtfsMarkIrpContextForQueue(IrpContext);
}
IrpContext->Irp->IoStatus.Information = 0;
return Status;
}
/* EOF */