2002-07-15 15:37:33 +00:00
|
|
|
/*
|
|
|
|
* ReactOS kernel
|
2014-11-02 21:50:40 +00:00
|
|
|
* Copyright (C) 2002, 2014 ReactOS Team
|
2002-07-15 15:37:33 +00:00
|
|
|
*
|
|
|
|
* 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
|
2008-03-08 21:45:51 +00:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
2002-07-15 15:37:33 +00:00
|
|
|
*
|
|
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS kernel
|
2008-02-10 11:20:29 +00:00
|
|
|
* FILE: drivers/filesystem/ntfs/mft.c
|
2002-07-15 15:37:33 +00:00
|
|
|
* PURPOSE: NTFS filesystem driver
|
2014-11-02 21:50:40 +00:00
|
|
|
* PROGRAMMERS: Eric Kohl
|
|
|
|
* Valentin Verkhovsky
|
|
|
|
* Pierre Schweitzer (pierre@reactos.org)
|
2014-11-02 22:56:38 +00:00
|
|
|
* Hervé Poussineau (hpoussin@reactos.org)
|
2002-07-15 15:37:33 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
|
2005-07-20 02:52:52 +00:00
|
|
|
#include "ntfs.h"
|
2002-07-15 15:37:33 +00:00
|
|
|
|
2004-06-05 08:28:37 +00:00
|
|
|
#define NDEBUG
|
2002-07-15 15:37:33 +00:00
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
PNTFS_ATTR_CONTEXT
|
|
|
|
PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
|
2002-07-15 15:37:33 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
PNTFS_ATTR_CONTEXT Context;
|
2003-11-12 15:30:21 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
Context = ExAllocatePoolWithTag(NonPagedPool,
|
|
|
|
FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length,
|
|
|
|
TAG_NTFS);
|
|
|
|
RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
|
|
|
|
if (AttrRecord->IsNonResident)
|
2002-07-15 15:37:33 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
LONGLONG DataRunOffset;
|
|
|
|
ULONGLONG DataRunLength;
|
|
|
|
|
|
|
|
Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
|
|
|
|
Context->CacheRunOffset = 0;
|
|
|
|
Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
|
|
|
|
Context->CacheRunLength = DataRunLength;
|
|
|
|
if (DataRunOffset != -1)
|
|
|
|
{
|
|
|
|
/* Normal run. */
|
|
|
|
Context->CacheRunStartLCN =
|
|
|
|
Context->CacheRunLastLCN = DataRunOffset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Sparse run. */
|
|
|
|
Context->CacheRunStartLCN = -1;
|
|
|
|
Context->CacheRunLastLCN = 0;
|
|
|
|
}
|
|
|
|
Context->CacheRunCurrentOffset = 0;
|
2002-07-15 15:37:33 +00:00
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
return Context;
|
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
VOID
|
|
|
|
ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
|
|
|
|
{
|
|
|
|
ExFreePoolWithTag(Context, TAG_NTFS);
|
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
NTSTATUS
|
|
|
|
FindAttribute(PDEVICE_EXTENSION Vcb,
|
|
|
|
PFILE_RECORD_HEADER MftRecord,
|
|
|
|
ULONG Type,
|
2014-10-16 20:05:36 +00:00
|
|
|
PCWSTR Name,
|
|
|
|
ULONG NameLength,
|
2014-09-26 13:57:29 +00:00
|
|
|
PNTFS_ATTR_CONTEXT * AttrCtx)
|
2003-09-15 16:01:16 +00:00
|
|
|
{
|
2015-09-04 15:52:19 +00:00
|
|
|
BOOLEAN 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
|
|
|
NTSTATUS Status;
|
|
|
|
FIND_ATTR_CONTXT Context;
|
|
|
|
PNTFS_ATTR_RECORD Attribute;
|
2002-07-15 15:37:33 +00:00
|
|
|
|
2014-10-26 19:09:52 +00:00
|
|
|
DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx);
|
2013-07-24 15:24:41 +00:00
|
|
|
|
2015-09-04 15:52:19 +00:00
|
|
|
Found = FALSE;
|
[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
|
|
|
Status = FindFirstAttribute(&Context, Vcb, MftRecord, FALSE, &Attribute);
|
|
|
|
while (NT_SUCCESS(Status))
|
2014-09-26 13:57:29 +00:00
|
|
|
{
|
[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
|
|
|
if (Attribute->Type == Type && Attribute->NameLength == NameLength)
|
|
|
|
{
|
2015-09-04 15:52:19 +00:00
|
|
|
if (NameLength != 0)
|
|
|
|
{
|
|
|
|
PWCHAR AttrName;
|
|
|
|
|
|
|
|
AttrName = (PWCHAR)((PCHAR)Attribute + Attribute->NameOffset);
|
|
|
|
DPRINT("%.*S, %.*S\n", Attribute->NameLength, AttrName, NameLength, Name);
|
|
|
|
if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1))
|
|
|
|
{
|
|
|
|
Found = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Found = TRUE;
|
|
|
|
}
|
[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
|
|
|
|
2015-09-04 15:52:19 +00:00
|
|
|
if (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
|
|
|
{
|
|
|
|
/* Found it, fill up the context and return. */
|
|
|
|
DPRINT("Found context\n");
|
|
|
|
*AttrCtx = PrepareAttributeContext(Attribute);
|
|
|
|
FindCloseAttribute(&Context);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = FindNextAttribute(&Context, &Attribute);
|
2003-11-12 15:30:21 +00:00
|
|
|
}
|
|
|
|
|
[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
|
|
|
FindCloseAttribute(&Context);
|
|
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
2003-09-15 16:01:16 +00:00
|
|
|
}
|
|
|
|
|
2002-07-15 15:37:33 +00:00
|
|
|
|
2003-11-12 15:30:21 +00:00
|
|
|
ULONG
|
2014-09-26 13:57:29 +00:00
|
|
|
AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
|
2003-11-12 15:30:21 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
if (AttrRecord->IsNonResident)
|
|
|
|
return AttrRecord->NonResident.AllocatedSize;
|
|
|
|
else
|
|
|
|
return AttrRecord->Resident.ValueLength;
|
|
|
|
}
|
|
|
|
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
ULONGLONG
|
|
|
|
AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
|
|
|
|
{
|
|
|
|
if (AttrRecord->IsNonResident)
|
|
|
|
return AttrRecord->NonResident.DataSize;
|
|
|
|
else
|
|
|
|
return AttrRecord->Resident.ValueLength;
|
2003-11-12 15:30:21 +00:00
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
|
|
|
|
2003-11-12 15:30:21 +00:00
|
|
|
ULONG
|
2014-09-26 13:57:29 +00:00
|
|
|
ReadAttribute(PDEVICE_EXTENSION Vcb,
|
|
|
|
PNTFS_ATTR_CONTEXT Context,
|
|
|
|
ULONGLONG Offset,
|
|
|
|
PCHAR Buffer,
|
|
|
|
ULONG Length)
|
2003-11-12 15:30:21 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
ULONGLONG LastLCN;
|
|
|
|
PUCHAR DataRun;
|
|
|
|
LONGLONG DataRunOffset;
|
|
|
|
ULONGLONG DataRunLength;
|
|
|
|
LONGLONG DataRunStartLCN;
|
|
|
|
ULONGLONG CurrentOffset;
|
|
|
|
ULONG ReadLength;
|
|
|
|
ULONG AlreadyRead;
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
if (!Context->Record.IsNonResident)
|
2003-11-12 15:30:21 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
if (Offset > Context->Record.Resident.ValueLength)
|
|
|
|
return 0;
|
|
|
|
if (Offset + Length > Context->Record.Resident.ValueLength)
|
|
|
|
Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
|
|
|
|
RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
|
|
|
|
return Length;
|
2003-11-12 15:30:21 +00:00
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
/*
|
|
|
|
* Non-resident attribute
|
|
|
|
*/
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
/*
|
|
|
|
* I. Find the corresponding start data run.
|
|
|
|
*/
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
AlreadyRead = 0;
|
2013-07-24 15:24:41 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
// FIXME: Cache seems to be non-working. Disable it for now
|
|
|
|
//if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
|
|
|
|
if (0)
|
|
|
|
{
|
|
|
|
DataRun = Context->CacheRun;
|
|
|
|
LastLCN = Context->CacheRunLastLCN;
|
|
|
|
DataRunStartLCN = Context->CacheRunStartLCN;
|
|
|
|
DataRunLength = Context->CacheRunLength;
|
|
|
|
CurrentOffset = Context->CacheRunCurrentOffset;
|
|
|
|
}
|
|
|
|
else
|
2003-11-12 15:30:21 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
LastLCN = 0;
|
|
|
|
DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
|
|
|
|
CurrentOffset = 0;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
|
|
|
|
if (DataRunOffset != -1)
|
|
|
|
{
|
|
|
|
/* Normal data run. */
|
|
|
|
DataRunStartLCN = LastLCN + DataRunOffset;
|
|
|
|
LastLCN = DataRunStartLCN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Sparse data run. */
|
|
|
|
DataRunStartLCN = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Offset >= CurrentOffset &&
|
|
|
|
Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*DataRun == 0)
|
|
|
|
{
|
|
|
|
return AlreadyRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
|
|
|
|
}
|
2003-11-12 15:30:21 +00:00
|
|
|
}
|
2002-07-15 15:37:33 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
/*
|
|
|
|
* II. Go through the run list and read the data
|
|
|
|
*/
|
|
|
|
|
|
|
|
ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
|
|
|
|
if (DataRunStartLCN == -1)
|
2016-04-14 20:39:55 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
RtlZeroMemory(Buffer, ReadLength);
|
2016-04-14 20:39:55 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Status = NtfsReadDisk(Vcb->StorageDevice,
|
|
|
|
DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
|
|
|
|
ReadLength,
|
|
|
|
Vcb->NtfsInfo.BytesPerSector,
|
|
|
|
(PVOID)Buffer,
|
|
|
|
FALSE);
|
|
|
|
}
|
2014-09-26 13:57:29 +00:00
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
Length -= ReadLength;
|
|
|
|
Buffer += ReadLength;
|
|
|
|
AlreadyRead += ReadLength;
|
|
|
|
|
|
|
|
if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
|
|
|
|
{
|
|
|
|
CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
|
|
|
|
DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
|
2016-03-26 13:15:14 +00:00
|
|
|
if (DataRunOffset != (ULONGLONG)-1)
|
2014-09-26 13:57:29 +00:00
|
|
|
{
|
|
|
|
DataRunStartLCN = LastLCN + DataRunOffset;
|
|
|
|
LastLCN = DataRunStartLCN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
DataRunStartLCN = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (Length > 0)
|
|
|
|
{
|
|
|
|
ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
|
|
|
|
if (DataRunStartLCN == -1)
|
|
|
|
RtlZeroMemory(Buffer, ReadLength);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Status = NtfsReadDisk(Vcb->StorageDevice,
|
|
|
|
DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
|
|
|
|
ReadLength,
|
2014-11-04 07:56:20 +00:00
|
|
|
Vcb->NtfsInfo.BytesPerSector,
|
2014-09-26 13:57:29 +00:00
|
|
|
(PVOID)Buffer,
|
|
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Length -= ReadLength;
|
|
|
|
Buffer += ReadLength;
|
|
|
|
AlreadyRead += ReadLength;
|
|
|
|
|
|
|
|
/* We finished this request, but there still data in this data run. */
|
|
|
|
if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Go to next run in the list.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (*DataRun == 0)
|
|
|
|
break;
|
|
|
|
CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
|
|
|
|
DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
|
|
|
|
if (DataRunOffset != -1)
|
|
|
|
{
|
|
|
|
/* Normal data run. */
|
|
|
|
DataRunStartLCN = LastLCN + DataRunOffset;
|
|
|
|
LastLCN = DataRunStartLCN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Sparse data run. */
|
|
|
|
DataRunStartLCN = -1;
|
|
|
|
}
|
|
|
|
} /* while */
|
|
|
|
|
|
|
|
} /* if Disk */
|
|
|
|
|
|
|
|
Context->CacheRun = DataRun;
|
|
|
|
Context->CacheRunOffset = Offset + AlreadyRead;
|
|
|
|
Context->CacheRunStartLCN = DataRunStartLCN;
|
|
|
|
Context->CacheRunLength = DataRunLength;
|
|
|
|
Context->CacheRunLastLCN = LastLCN;
|
|
|
|
Context->CacheRunCurrentOffset = CurrentOffset;
|
|
|
|
|
|
|
|
return AlreadyRead;
|
2002-07-15 15:37:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-05 08:28:37 +00:00
|
|
|
NTSTATUS
|
2013-06-16 12:15:06 +00:00
|
|
|
ReadFileRecord(PDEVICE_EXTENSION Vcb,
|
2014-09-26 13:57:29 +00:00
|
|
|
ULONGLONG index,
|
|
|
|
PFILE_RECORD_HEADER file)
|
2003-09-15 16:01:16 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
ULONGLONG BytesRead;
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2014-10-26 19:09:52 +00:00
|
|
|
DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
|
2014-10-17 06:55:52 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
|
|
|
|
if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
|
|
|
|
{
|
2014-10-26 19:09:52 +00:00
|
|
|
DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
|
2014-09-26 13:57:29 +00:00
|
|
|
return STATUS_PARTIAL_COPY;
|
|
|
|
}
|
2006-01-07 06:20:59 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
/* Apply update sequence array fixups. */
|
|
|
|
return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
|
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
NTSTATUS
|
|
|
|
FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
|
|
|
|
PNTFS_RECORD_HEADER Record)
|
|
|
|
{
|
|
|
|
USHORT *USA;
|
|
|
|
USHORT USANumber;
|
|
|
|
USHORT USACount;
|
|
|
|
USHORT *Block;
|
|
|
|
|
|
|
|
USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
|
|
|
|
USANumber = *(USA++);
|
|
|
|
USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
|
|
|
|
Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
while (USACount)
|
|
|
|
{
|
|
|
|
if (*Block != USANumber)
|
|
|
|
{
|
|
|
|
DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
*Block = *(USA++);
|
|
|
|
Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
|
|
|
|
USACount--;
|
|
|
|
}
|
2004-06-05 08:28:37 +00:00
|
|
|
|
2013-06-16 12:15:06 +00:00
|
|
|
return STATUS_SUCCESS;
|
2003-09-15 16:01:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
NTSTATUS
|
|
|
|
ReadLCN(PDEVICE_EXTENSION Vcb,
|
|
|
|
ULONGLONG lcn,
|
|
|
|
ULONG count,
|
|
|
|
PVOID buffer)
|
2003-09-15 16:01:16 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
LARGE_INTEGER DiskSector;
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
DiskSector.QuadPart = lcn;
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
return NtfsReadSectors(Vcb->StorageDevice,
|
|
|
|
DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
|
|
|
|
count * Vcb->NtfsInfo.SectorsPerCluster,
|
|
|
|
Vcb->NtfsInfo.BytesPerSector,
|
|
|
|
buffer,
|
|
|
|
FALSE);
|
|
|
|
}
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2003-11-12 15:30:21 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
BOOLEAN
|
|
|
|
CompareFileName(PUNICODE_STRING FileName,
|
2014-10-14 18:41:38 +00:00
|
|
|
PINDEX_ENTRY_ATTRIBUTE IndexEntry,
|
|
|
|
BOOLEAN DirSearch)
|
2014-09-26 13:57:29 +00:00
|
|
|
{
|
2014-11-23 15:49:24 +00:00
|
|
|
BOOLEAN Ret, Alloc = FALSE;
|
2014-09-26 13:57:29 +00:00
|
|
|
UNICODE_STRING EntryName;
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
EntryName.Buffer = IndexEntry->FileName.Name;
|
|
|
|
EntryName.Length =
|
2014-10-27 12:35:58 +00:00
|
|
|
EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2014-10-14 18:41:38 +00:00
|
|
|
if (DirSearch)
|
|
|
|
{
|
2014-11-23 15:49:24 +00:00
|
|
|
UNICODE_STRING IntFileName;
|
|
|
|
if (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)
|
|
|
|
{
|
2014-11-24 19:54:22 +00:00
|
|
|
NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName, FileName, TRUE)));
|
2014-11-23 15:49:24 +00:00
|
|
|
Alloc = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IntFileName = *FileName;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX), NULL);
|
|
|
|
|
|
|
|
if (Alloc)
|
|
|
|
{
|
|
|
|
RtlFreeUnicodeString(&IntFileName);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ret;
|
2014-10-14 18:41:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-10-27 12:35:58 +00:00
|
|
|
return (RtlCompareUnicodeString(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == 0);
|
2014-10-14 18:41:38 +00:00
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
}
|
|
|
|
|
2015-07-05 09:04:24 +00:00
|
|
|
#if 0
|
|
|
|
static
|
|
|
|
VOID
|
|
|
|
DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
|
|
|
|
{
|
|
|
|
DPRINT1("Entry: %p\n", IndexEntry);
|
|
|
|
DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry->Data.Directory.IndexedFile);
|
|
|
|
DPRINT1("\tLength: %u\n", IndexEntry->Length);
|
|
|
|
DPRINT1("\tKeyLength: %u\n", IndexEntry->KeyLength);
|
|
|
|
DPRINT1("\tFlags: %x\n", IndexEntry->Flags);
|
|
|
|
DPRINT1("\tReserved: %x\n", IndexEntry->Reserved);
|
|
|
|
DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry->FileName.DirectoryFileReferenceNumber);
|
|
|
|
DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry->FileName.CreationTime);
|
|
|
|
DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry->FileName.ChangeTime);
|
|
|
|
DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry->FileName.LastWriteTime);
|
|
|
|
DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry->FileName.LastAccessTime);
|
|
|
|
DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry->FileName.AllocatedSize);
|
|
|
|
DPRINT1("\t\tDataSize: %I64u\n", IndexEntry->FileName.DataSize);
|
|
|
|
DPRINT1("\t\tFileAttributes: %x\n", IndexEntry->FileName.FileAttributes);
|
|
|
|
DPRINT1("\t\tNameLength: %u\n", IndexEntry->FileName.NameLength);
|
|
|
|
DPRINT1("\t\tNameType: %x\n", IndexEntry->FileName.NameType);
|
|
|
|
DPRINT1("\t\tName: %.*S\n", IndexEntry->FileName.NameLength, IndexEntry->FileName.Name);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-01-08 20:30:10 +00:00
|
|
|
NTSTATUS
|
2015-07-04 19:52:31 +00:00
|
|
|
BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
|
|
|
|
PFILE_RECORD_HEADER MftRecord,
|
|
|
|
PCHAR IndexRecord,
|
|
|
|
ULONG IndexBlockSize,
|
|
|
|
PINDEX_ENTRY_ATTRIBUTE FirstEntry,
|
2015-01-08 20:30:10 +00:00
|
|
|
PINDEX_ENTRY_ATTRIBUTE LastEntry,
|
|
|
|
PUNICODE_STRING FileName,
|
|
|
|
PULONG StartEntry,
|
|
|
|
PULONG CurrentEntry,
|
|
|
|
BOOLEAN DirSearch,
|
|
|
|
ULONGLONG *OutMFTIndex)
|
|
|
|
{
|
2015-07-04 19:52:31 +00:00
|
|
|
NTSTATUS Status;
|
|
|
|
ULONG RecordOffset;
|
2015-01-08 20:30:10 +00:00
|
|
|
PINDEX_ENTRY_ATTRIBUTE IndexEntry;
|
2015-07-04 19:52:31 +00:00
|
|
|
PNTFS_ATTR_CONTEXT IndexAllocationCtx;
|
|
|
|
ULONGLONG IndexAllocationSize;
|
|
|
|
PINDEX_BUFFER IndexBuffer;
|
|
|
|
|
|
|
|
DPRINT("BrowseIndexEntries(%p, %p, %p, %u, %p, %p, %wZ, %u, %u, %u, %p)\n", Vcb, MftRecord, IndexRecord, IndexBlockSize, FirstEntry, LastEntry, FileName, *StartEntry, *CurrentEntry, DirSearch, OutMFTIndex);
|
2015-01-08 20:30:10 +00:00
|
|
|
|
|
|
|
IndexEntry = FirstEntry;
|
|
|
|
while (IndexEntry < LastEntry &&
|
|
|
|
!(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
|
|
|
|
{
|
|
|
|
if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
|
|
|
|
*CurrentEntry >= *StartEntry &&
|
2015-07-05 09:04:24 +00:00
|
|
|
IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
|
2015-01-08 20:30:10 +00:00
|
|
|
CompareFileName(FileName, IndexEntry, DirSearch))
|
|
|
|
{
|
|
|
|
*StartEntry = *CurrentEntry;
|
|
|
|
*OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*CurrentEntry) += 1;
|
|
|
|
ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
|
|
|
|
IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
|
|
|
|
}
|
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
/* If we're already browsing a subnode */
|
|
|
|
if (IndexRecord == NULL)
|
|
|
|
{
|
|
|
|
return STATUS_OBJECT_PATH_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there's no subnode */
|
2015-06-24 19:21:04 +00:00
|
|
|
if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
|
|
|
|
{
|
|
|
|
return STATUS_OBJECT_PATH_NOT_FOUND;
|
|
|
|
}
|
2015-01-08 20:30:10 +00:00
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT("Corrupted filesystem!\n");
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
|
|
|
|
Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
|
|
for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
|
|
|
|
{
|
|
|
|
ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
|
|
|
|
Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
IndexBuffer = (PINDEX_BUFFER)IndexRecord;
|
|
|
|
ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
|
|
|
|
ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
|
|
|
|
FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
|
|
|
|
LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
|
|
|
|
ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
|
|
|
|
|
|
|
|
Status = BrowseIndexEntries(NULL, NULL, NULL, 0, FirstEntry, LastEntry, FileName, StartEntry, CurrentEntry, DirSearch, OutMFTIndex);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseAttributeContext(IndexAllocationCtx);
|
|
|
|
return Status;
|
2015-01-08 20:30:10 +00:00
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
NTSTATUS
|
2014-10-14 18:41:38 +00:00
|
|
|
NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
|
|
|
|
ULONGLONG MFTIndex,
|
|
|
|
PUNICODE_STRING FileName,
|
2014-10-14 20:34:38 +00:00
|
|
|
PULONG FirstEntry,
|
2014-10-14 18:41:38 +00:00
|
|
|
BOOLEAN DirSearch,
|
2014-10-18 12:18:37 +00:00
|
|
|
ULONGLONG *OutMFTIndex)
|
2003-09-15 16:01:16 +00:00
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
PFILE_RECORD_HEADER MftRecord;
|
|
|
|
PNTFS_ATTR_CONTEXT IndexRootCtx;
|
|
|
|
PINDEX_ROOT_ATTRIBUTE IndexRoot;
|
|
|
|
PCHAR IndexRecord;
|
|
|
|
PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
|
|
|
|
NTSTATUS Status;
|
2014-10-14 19:11:49 +00:00
|
|
|
ULONG CurrentEntry = 0;
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %u, %u, %p)\n", Vcb, MFTIndex, FileName, *FirstEntry, DirSearch, OutMFTIndex);
|
2014-10-16 20:05:36 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
MftRecord = ExAllocatePoolWithTag(NonPagedPool,
|
|
|
|
Vcb->NtfsInfo.BytesPerFileRecord,
|
|
|
|
TAG_NTFS);
|
|
|
|
if (MftRecord == NULL)
|
|
|
|
{
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
Status = ReadFileRecord(Vcb, MFTIndex, MftRecord);
|
|
|
|
if (!NT_SUCCESS(Status))
|
2014-09-26 13:57:29 +00:00
|
|
|
{
|
2015-07-04 19:52:31 +00:00
|
|
|
ExFreePoolWithTag(MftRecord, TAG_NTFS);
|
|
|
|
return Status;
|
|
|
|
}
|
2014-09-26 13:57:29 +00:00
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
|
|
|
|
Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
ExFreePoolWithTag(MftRecord, TAG_NTFS);
|
|
|
|
return Status;
|
|
|
|
}
|
2014-09-26 13:57:29 +00:00
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
|
|
|
|
if (IndexRecord == NULL)
|
|
|
|
{
|
2014-09-26 13:57:29 +00:00
|
|
|
ReleaseAttributeContext(IndexRootCtx);
|
2015-07-04 19:52:31 +00:00
|
|
|
ExFreePoolWithTag(MftRecord, TAG_NTFS);
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
2003-11-13 15:26:34 +00:00
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
|
|
|
|
IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
|
|
|
|
IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
|
|
|
|
/* Index root is always resident. */
|
|
|
|
IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
|
|
|
|
ReleaseAttributeContext(IndexRootCtx);
|
2014-09-26 13:57:29 +00:00
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
|
2014-09-26 13:57:29 +00:00
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
Status = BrowseIndexEntries(Vcb, MftRecord, IndexRecord, IndexRoot->SizeOfEntry, IndexEntry, IndexEntryEnd, FileName, FirstEntry, &CurrentEntry, DirSearch, OutMFTIndex);
|
2003-11-13 15:26:34 +00:00
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
ExFreePoolWithTag(IndexRecord, TAG_NTFS);
|
2014-09-26 13:57:29 +00:00
|
|
|
ExFreePoolWithTag(MftRecord, TAG_NTFS);
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2015-07-04 19:52:31 +00:00
|
|
|
return Status;
|
2003-09-15 16:01:16 +00:00
|
|
|
}
|
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
NTSTATUS
|
2014-09-26 18:12:24 +00:00
|
|
|
NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
|
|
|
|
PUNICODE_STRING PathName,
|
|
|
|
PFILE_RECORD_HEADER *FileRecord,
|
2014-10-08 19:12:48 +00:00
|
|
|
PULONGLONG MFTIndex,
|
2014-09-26 18:12:24 +00:00
|
|
|
ULONGLONG CurrentMFTIndex)
|
2003-09-15 16:01:16 +00:00
|
|
|
{
|
2014-10-16 20:05:36 +00:00
|
|
|
UNICODE_STRING Current, Remaining;
|
2014-09-26 13:57:29 +00:00
|
|
|
NTSTATUS Status;
|
2014-10-18 12:18:37 +00:00
|
|
|
ULONG FirstEntry = 0;
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2015-06-27 08:55:34 +00:00
|
|
|
DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %I64x)\n", Vcb, PathName, FileRecord, CurrentMFTIndex);
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
FsRtlDissectName(*PathName, &Current, &Remaining);
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
while (Current.Length != 0)
|
2013-06-16 12:15:06 +00:00
|
|
|
{
|
2014-10-26 19:09:52 +00:00
|
|
|
DPRINT("Current: %wZ\n", &Current);
|
2014-09-26 13:57:29 +00:00
|
|
|
|
2014-10-18 12:18:37 +00:00
|
|
|
Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
|
2014-09-26 13:57:29 +00:00
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2014-10-17 22:17:59 +00:00
|
|
|
if (Remaining.Length == 0)
|
2014-10-27 12:35:58 +00:00
|
|
|
break;
|
2014-10-17 22:17:59 +00:00
|
|
|
|
|
|
|
FsRtlDissectName(Current, &Current, &Remaining);
|
2013-06-16 12:15:06 +00:00
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
*FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
|
|
|
|
if (*FileRecord == NULL)
|
|
|
|
{
|
2014-10-14 19:11:49 +00:00
|
|
|
DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
|
2014-09-26 13:57:29 +00:00
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2014-10-14 19:11:49 +00:00
|
|
|
DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
|
2014-10-17 06:55:52 +00:00
|
|
|
ExFreePoolWithTag(*FileRecord, TAG_NTFS);
|
2014-09-26 13:57:29 +00:00
|
|
|
return Status;
|
|
|
|
}
|
2003-09-15 16:01:16 +00:00
|
|
|
|
2014-10-08 19:12:48 +00:00
|
|
|
*MFTIndex = CurrentMFTIndex;
|
|
|
|
|
2014-09-26 13:57:29 +00:00
|
|
|
return STATUS_SUCCESS;
|
2003-09-15 16:01:16 +00:00
|
|
|
}
|
2014-09-26 18:12:24 +00:00
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NtfsLookupFile(PDEVICE_EXTENSION Vcb,
|
|
|
|
PUNICODE_STRING PathName,
|
|
|
|
PFILE_RECORD_HEADER *FileRecord,
|
2014-10-08 19:12:48 +00:00
|
|
|
PULONGLONG MFTIndex)
|
2014-09-26 18:12:24 +00:00
|
|
|
{
|
2015-06-27 08:55:34 +00:00
|
|
|
return NtfsLookupFileAt(Vcb, PathName, FileRecord, MFTIndex, NTFS_FILE_ROOT);
|
2014-09-26 18:12:24 +00:00
|
|
|
}
|
2014-10-14 19:11:49 +00:00
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
|
|
|
|
PUNICODE_STRING SearchPattern,
|
2014-10-14 20:34:38 +00:00
|
|
|
PULONG FirstEntry,
|
2014-10-14 19:11:49 +00:00
|
|
|
PFILE_RECORD_HEADER *FileRecord,
|
|
|
|
PULONGLONG MFTIndex,
|
|
|
|
ULONGLONG CurrentMFTIndex)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
2015-06-25 20:51:30 +00:00
|
|
|
DPRINT("NtfsFindFileAt(%p, %wZ, %u, %p, %p, %I64x)\n", Vcb, SearchPattern, *FirstEntry, FileRecord, MFTIndex, CurrentMFTIndex);
|
2014-10-14 19:11:49 +00:00
|
|
|
|
2014-10-18 12:18:37 +00:00
|
|
|
Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, &CurrentMFTIndex);
|
2014-10-14 19:11:49 +00:00
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2014-10-26 19:09:52 +00:00
|
|
|
DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
|
2014-10-14 19:11:49 +00:00
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
*FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
|
|
|
|
if (*FileRecord == NULL)
|
|
|
|
{
|
|
|
|
DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT("NtfsFindFileAt: Can't read MFT record\n");
|
2014-10-17 06:55:52 +00:00
|
|
|
ExFreePoolWithTag(*FileRecord, TAG_NTFS);
|
2014-10-14 19:11:49 +00:00
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
*MFTIndex = CurrentMFTIndex;
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2003-11-12 15:30:21 +00:00
|
|
|
/* EOF */
|