You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3250 lines
109 KiB
3250 lines
109 KiB
/*++
|
|
|
|
(c) 1998 Seagate Software, Inc. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
fsaitemr.cpp
|
|
|
|
Abstract:
|
|
|
|
This class contains represents a scan item (i.e. file or directory) for NTFS 5.0.
|
|
|
|
Author:
|
|
|
|
Chuck Bardeen [cbardeen] 1-Dec-1996
|
|
|
|
Revision History:
|
|
Michael Lotz [lotz ] 13-Jan-1997
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#define WSB_TRACE_IS WSB_TRACE_BIT_FSA
|
|
|
|
#include "wsb.h"
|
|
#include "fsa.h"
|
|
#include "fsaitem.h"
|
|
#include "rpdata.h"
|
|
#include "rpio.h"
|
|
#include "rpguid.h"
|
|
#include "fsaitemr.h"
|
|
|
|
|
|
#define SHARE_FLAGS (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
|
|
#define EXCLUSIVE_FLAG ( 0 ) // exclusive open without sharing of the file
|
|
|
|
//
|
|
// Notice these two bits are NOT the same location????
|
|
//
|
|
#define BIT_FOR_TRUNCATED FILE_ATTRIBUTE_OFFLINE
|
|
#define BIT_FOR_RP FILE_ATTRIBUTE_REPARSE_POINT
|
|
|
|
//
|
|
// File extensions that are treated as special cases for truncate
|
|
//
|
|
#define EXT_FOR_EXE L".exe"
|
|
#define EXT_FOR_DLL L".dll"
|
|
|
|
//
|
|
// Macros and defines for exe and dll headers
|
|
//
|
|
#define SIZE_OF_NT_SIGNATURE sizeof(DWORD)
|
|
//
|
|
// Macros
|
|
//
|
|
/* Offset to PE file signature */
|
|
#define NTSIGNATURE(a) ((LPVOID)((BYTE *)a + \
|
|
((PIMAGE_DOS_HEADER)a)->e_lfanew))
|
|
|
|
/* MS-OS header identifies the NT PEFile signature dword;
|
|
the PEFILE header exists just after that dword. */
|
|
#define PEFHDROFFSET(a) ((LPVOID)((BYTE *)a + \
|
|
((PIMAGE_DOS_HEADER)a)->e_lfanew + \
|
|
SIZE_OF_NT_SIGNATURE))
|
|
|
|
/* PE optional header is immediately after PEFile header. */
|
|
#define OPTHDROFFSET(a) ((LPVOID)((BYTE *)a + \
|
|
((PIMAGE_DOS_HEADER)a)->e_lfanew + \
|
|
SIZE_OF_NT_SIGNATURE + \
|
|
sizeof (IMAGE_FILE_HEADER)))
|
|
|
|
/* Section headers are immediately after PE optional header. */
|
|
#define SECHDROFFSET(a) ((LPVOID)((BYTE *)a + \
|
|
((PIMAGE_DOS_HEADER)a)->e_lfanew + \
|
|
SIZE_OF_NT_SIGNATURE + \
|
|
sizeof (IMAGE_FILE_HEADER) + \
|
|
sizeof (IMAGE_OPTIONAL_HEADER)))
|
|
|
|
|
|
|
|
HRESULT
|
|
OpenObject (
|
|
IN WCHAR const *pwszFile,
|
|
IN ULONG CreateOptions,
|
|
IN ULONG DesiredAccess,
|
|
IN ULONG ShareAccess,
|
|
IN ULONG CreateDisposition,
|
|
OUT IO_STATUS_BLOCK *IoStatusBlock,
|
|
OUT HANDLE *ObjectHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements: A wrapper function for NtCreateFile
|
|
|
|
OpenObject
|
|
|
|
--*/
|
|
//
|
|
// Simple wrapper for NtCreateFile
|
|
//
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
NTSTATUS ntStatus;
|
|
BOOL bStatus;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING str;
|
|
RTL_RELATIVE_NAME_U RelativeName;
|
|
PVOID StrBuffer = NULL;
|
|
BOOL bReleaseRelativeName = FALSE;
|
|
|
|
WsbTraceIn(OLESTR("OpenObject"), OLESTR(""));
|
|
//
|
|
// Null out the pointer so we know when it was allocated
|
|
//
|
|
str.Buffer = NULL;
|
|
RelativeName.RelativeName.Buffer = NULL;
|
|
|
|
try {
|
|
//
|
|
// Convert input name into special format with \??\
|
|
//
|
|
//bStatus = RtlDosPathNameToNtPathName_U( pwszFile,
|
|
// &str,
|
|
// NULL,
|
|
// NULL );
|
|
//WsbAffirm( bStatus, E_FAIL);
|
|
|
|
bStatus = RtlDosPathNameToRelativeNtPathName_U(
|
|
pwszFile,
|
|
&str,
|
|
NULL,
|
|
&RelativeName
|
|
);
|
|
|
|
WsbAffirm( bStatus, E_FAIL);
|
|
bReleaseRelativeName = TRUE;
|
|
StrBuffer = str.Buffer;
|
|
|
|
if ( RelativeName.RelativeName.Length ) {
|
|
str = RelativeName.RelativeName;
|
|
} else {
|
|
RelativeName.ContainingDirectory = NULL;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&str,
|
|
0,
|
|
RelativeName.ContainingDirectory,
|
|
NULL
|
|
);
|
|
|
|
|
|
ntStatus = NtCreateFile(
|
|
ObjectHandle,
|
|
DesiredAccess | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
IoStatusBlock,
|
|
NULL, // pallocationsize (none!)
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
ShareAccess,
|
|
CreateDisposition,
|
|
CreateOptions | FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL, // EA buffer (none!)
|
|
0);
|
|
//
|
|
// Right now if the file is not a reparse point the above open
|
|
// fails -- so now try it without the FILE_OPEN_REPARSE_POINT
|
|
//
|
|
if ( STATUS_NOT_A_REPARSE_POINT == ntStatus) {
|
|
WsbAffirmNtStatus( NtCreateFile(
|
|
ObjectHandle,
|
|
DesiredAccess | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
IoStatusBlock,
|
|
NULL, // pallocationsize (none!)
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
ShareAccess,
|
|
CreateDisposition,
|
|
CreateOptions | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_POSIX_SEMANTICS,
|
|
NULL, // EA buffer (none!)
|
|
0 ) );
|
|
} else {
|
|
WsbAffirmNtStatus( ntStatus );
|
|
}
|
|
|
|
} WsbCatch( hr );
|
|
|
|
//
|
|
// Clean up the memory if we allocated it
|
|
//
|
|
if (bReleaseRelativeName) {
|
|
RtlReleaseRelativeName(&RelativeName);
|
|
}
|
|
|
|
if (NULL != StrBuffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, StrBuffer);
|
|
}
|
|
|
|
//if( NULL == str.Buffer ) {
|
|
// bStatus = RtlFreeHeap( RtlProcessHeap(), 0, str.Buffer );
|
|
//}
|
|
|
|
WsbTraceOut(OLESTR("OpenObject"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return( hr );
|
|
} // OpenObject
|
|
|
|
|
|
HRESULT
|
|
CopyPlaceholderToRP (
|
|
IN CONST FSA_PLACEHOLDER *pPlaceholder,
|
|
OUT PREPARSE_DATA_BUFFER pReparseBuffer,
|
|
IN BOOL bTruncated
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements: A wrapper function for copying the placeholder data into the reparse point
|
|
|
|
CopyPlaceholderToRP
|
|
|
|
--*/
|
|
//
|
|
// Simple wrapper moving the data from the scan item in-memory
|
|
// placeholder information into a reparse point buffer
|
|
//
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PRP_DATA pHsmData;
|
|
|
|
WsbTraceIn(OLESTR("CopyPlaceholderToRP"), OLESTR(""));
|
|
WsbTrace(OLESTR(" fileStart = %I64d, dataStart = %I64d, dataStreamStart = %I64d\n"),
|
|
pPlaceholder->fileStart, pPlaceholder->dataStart,
|
|
pPlaceholder->dataStreamStart);
|
|
WsbTrace(OLESTR(" fileSize = %I64d, dataSize = %I64d, dataStreamSize = %I64d\n"),
|
|
pPlaceholder->fileSize, pPlaceholder->dataSize,
|
|
pPlaceholder->dataStreamSize);
|
|
try {
|
|
//
|
|
// Validate the pointers passed in
|
|
//
|
|
WsbAssert( NULL != pPlaceholder, E_POINTER );
|
|
WsbAssert( NULL != pReparseBuffer, E_POINTER );
|
|
|
|
//
|
|
// Setup the pointer to our hsm data
|
|
//
|
|
pHsmData = (PRP_DATA) &pReparseBuffer->GenericReparseBuffer.DataBuffer[0];
|
|
|
|
//
|
|
// Set the generic reparse point header information for the tag and size
|
|
//
|
|
pReparseBuffer->ReparseTag = IO_REPARSE_TAG_HSM ;
|
|
pReparseBuffer->ReparseDataLength = sizeof(RP_DATA);
|
|
pReparseBuffer->Reserved = 0 ;
|
|
|
|
//
|
|
// Set the private data that is the vendor id and version number
|
|
//
|
|
pHsmData->vendorId = RP_MSFT_VENDOR_ID;
|
|
pHsmData->version = RP_VERSION;
|
|
|
|
//
|
|
// Assume for now that there is only one placeholder
|
|
// This needs to be updated
|
|
//
|
|
pHsmData->numPrivateData = 1;
|
|
pHsmData->fileIdentifier = GUID_NULL;
|
|
|
|
|
|
ZeroMemory(pHsmData->data.reserved, RP_RESV_SIZE);
|
|
//
|
|
// If the file is to indicate the file is truncated then set the bit
|
|
// otherwise make sure it is off
|
|
//
|
|
RP_INIT_BITFLAG( pHsmData->data.bitFlags );
|
|
if( bTruncated ) {
|
|
RP_SET_TRUNCATED_BIT( pHsmData->data.bitFlags );
|
|
} else {
|
|
RP_CLEAR_TRUNCATED_BIT( pHsmData->data.bitFlags );
|
|
}
|
|
|
|
//
|
|
// Set the truncate on close bit as needed
|
|
//
|
|
if( pPlaceholder->truncateOnClose ) {
|
|
RP_SET_TRUNCATE_ON_CLOSE_BIT( pHsmData->data.bitFlags );
|
|
} else {
|
|
RP_CLEAR_TRUNCATE_ON_CLOSE_BIT( pHsmData->data.bitFlags );
|
|
}
|
|
|
|
//
|
|
// Set the Premigrate on close bit as needed
|
|
//
|
|
if( pPlaceholder->premigrateOnClose ) {
|
|
RP_SET_PREMIGRATE_ON_CLOSE_BIT( pHsmData->data.bitFlags );
|
|
} else {
|
|
RP_CLEAR_PREMIGRATE_ON_CLOSE_BIT( pHsmData->data.bitFlags );
|
|
}
|
|
|
|
//
|
|
// Set the global bit flags based on the placeholder data
|
|
// For now since we are assuming one placeholder then set
|
|
// them the same.
|
|
pHsmData->globalBitFlags = pHsmData->data.bitFlags;
|
|
|
|
//
|
|
// Move over the data parts of the information
|
|
//
|
|
pHsmData->data.migrationTime.QuadPart = WsbFTtoLL( pPlaceholder->migrationTime );
|
|
pHsmData->data.hsmId = pPlaceholder->hsmId;
|
|
pHsmData->data.bagId = pPlaceholder->bagId;
|
|
pHsmData->data.fileStart.QuadPart = pPlaceholder->fileStart;
|
|
pHsmData->data.fileSize.QuadPart = pPlaceholder->fileSize;
|
|
pHsmData->data.dataStart.QuadPart = pPlaceholder->dataStart;
|
|
pHsmData->data.dataSize.QuadPart = pPlaceholder->dataSize;
|
|
pHsmData->data.fileVersionId.QuadPart = pPlaceholder->fileVersionId;
|
|
pHsmData->data.verificationData.QuadPart = pPlaceholder->verificationData;
|
|
pHsmData->data.verificationType = pPlaceholder->verificationType;
|
|
pHsmData->data.recallCount = pPlaceholder->recallCount;
|
|
pHsmData->data.recallTime.QuadPart = WsbFTtoLL( pPlaceholder->recallTime );
|
|
pHsmData->data.dataStreamStart.QuadPart = pPlaceholder->dataStreamStart;
|
|
pHsmData->data.dataStreamSize.QuadPart = pPlaceholder->dataStreamSize;
|
|
pHsmData->data.dataStream = pPlaceholder->dataStream;
|
|
|
|
pHsmData->data.dataStreamCRCType = pPlaceholder->dataStreamCRCType;
|
|
pHsmData->data.dataStreamCRC.QuadPart = pPlaceholder->dataStreamCRC;
|
|
//
|
|
// Lastly generate the check sum
|
|
//
|
|
RP_GEN_QUALIFIER(pHsmData, pHsmData->qualifier);
|
|
|
|
//
|
|
// Now set the bit that tells the filter that it is us setting the reparse point.
|
|
// This is not included in the qualifier checksum generation.
|
|
//
|
|
RP_SET_ORIGINATOR_BIT( pHsmData->data.bitFlags );
|
|
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CopyPlaceholderToRP"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return( hr );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT
|
|
CopyRPDataToPlaceholder (
|
|
IN CONST PRP_DATA pHsmData,
|
|
OUT FSA_PLACEHOLDER *pPlaceholder
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements: A wrapper function for moving the Reparse Point into generic FSA_PLACEHOLDER format
|
|
|
|
CopyRPDataToPlaceholder
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG qualifier; // Used to checksum the data
|
|
|
|
WsbTraceIn(OLESTR("CopyRPDataToPlaceholder"), OLESTR(""));
|
|
WsbTrace(OLESTR(" fileStart = %I64d, dataStart = %I64d, dataStreamStart = %I64d\n"),
|
|
pHsmData->data.fileStart.QuadPart, pHsmData->data.dataStart.QuadPart,
|
|
pHsmData->data.dataStreamStart.QuadPart);
|
|
WsbTrace(OLESTR(" fileSize = %I64d, dataSize = %I64d, dataStreamSize = %I64d\n"),
|
|
pHsmData->data.fileSize.QuadPart, pHsmData->data.dataSize.QuadPart,
|
|
pHsmData->data.dataStreamSize.QuadPart);
|
|
//
|
|
// Simple wrapper moving the data from the reparse point buffer into the
|
|
// generic placeholder information
|
|
//
|
|
try {
|
|
//
|
|
// Validate the pointers passed in
|
|
//
|
|
WsbAssert( NULL != pHsmData, E_POINTER );
|
|
WsbAssert( NULL != pPlaceholder, E_POINTER );
|
|
|
|
//
|
|
// Just in case, we clear out the originator bit.
|
|
//
|
|
RP_CLEAR_ORIGINATOR_BIT( pHsmData->data.bitFlags );
|
|
|
|
//
|
|
// Verify the check sum and the key private fields
|
|
//
|
|
RP_GEN_QUALIFIER(pHsmData, qualifier);
|
|
WsbAffirm( pHsmData->qualifier == qualifier, E_FAIL );
|
|
WsbAffirm( RP_MSFT_VENDOR_ID == pHsmData->vendorId, E_FAIL );
|
|
WsbAffirm( RP_VERSION == pHsmData->version, E_FAIL );
|
|
|
|
//
|
|
// Now that everything worked, save the values in our private data
|
|
//
|
|
pPlaceholder->migrationTime = WsbLLtoFT( pHsmData->data.migrationTime.QuadPart );
|
|
pPlaceholder->hsmId = pHsmData->data.hsmId;
|
|
pPlaceholder->bagId = pHsmData->data.bagId;
|
|
pPlaceholder->fileStart = pHsmData->data.fileStart.QuadPart;
|
|
pPlaceholder->fileSize = pHsmData->data.fileSize.QuadPart;
|
|
pPlaceholder->dataStart = pHsmData->data.dataStart.QuadPart;
|
|
pPlaceholder->dataSize = pHsmData->data.dataSize.QuadPart;
|
|
pPlaceholder->fileVersionId = pHsmData->data.fileVersionId.QuadPart;
|
|
pPlaceholder->verificationData = pHsmData->data.verificationData.QuadPart;
|
|
pPlaceholder->verificationType = pHsmData->data.verificationType;
|
|
pPlaceholder->recallCount = pHsmData->data.recallCount;
|
|
pPlaceholder->recallTime = WsbLLtoFT( pHsmData->data.recallTime.QuadPart );
|
|
pPlaceholder->dataStreamStart = pHsmData->data.dataStreamStart.QuadPart;
|
|
pPlaceholder->dataStreamSize = pHsmData->data.dataStreamSize.QuadPart;
|
|
pPlaceholder->dataStream = pHsmData->data.dataStream;
|
|
pPlaceholder->dataStreamCRCType = pHsmData->data.dataStreamCRCType;
|
|
pPlaceholder->dataStreamCRC = pHsmData->data.dataStreamCRC.QuadPart;
|
|
|
|
//
|
|
// Set placeholder bits
|
|
//
|
|
if( RP_FILE_IS_TRUNCATED( pHsmData->data.bitFlags ) ) {
|
|
pPlaceholder->isTruncated = TRUE;
|
|
} else {
|
|
pPlaceholder->isTruncated = FALSE;
|
|
}
|
|
|
|
if( RP_FILE_DO_TRUNCATE_ON_CLOSE( pHsmData->data.bitFlags ) ) {
|
|
pPlaceholder->truncateOnClose = TRUE;
|
|
} else {
|
|
pPlaceholder->truncateOnClose = FALSE;
|
|
}
|
|
|
|
if( RP_FILE_DO_PREMIGRATE_ON_CLOSE( pHsmData->data.bitFlags ) ) {
|
|
pPlaceholder->premigrateOnClose = TRUE;
|
|
} else {
|
|
pPlaceholder->premigrateOnClose = FALSE;
|
|
}
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CopyRPDataToPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return( hr );
|
|
}
|
|
|
|
HRESULT
|
|
CopyRPToPlaceholder (
|
|
IN CONST PREPARSE_DATA_BUFFER pReparseBuffer,
|
|
OUT FSA_PLACEHOLDER *pPlaceholder
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements: A wrapper function for moving the Reparse Point into generic FSA_PLACEHOLDER format
|
|
|
|
CopyRPToPlaceholder
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PRP_DATA pHsmData;
|
|
|
|
WsbTraceIn(OLESTR("CopyRPToPlaceholder"), OLESTR(""));
|
|
//
|
|
// Simple wrapper moving the data from the reparse point buffer into the
|
|
// generic placeholder information
|
|
//
|
|
try {
|
|
//
|
|
// Validate the pointers passed in
|
|
//
|
|
WsbAssert( NULL != pReparseBuffer, E_POINTER );
|
|
WsbAssert( NULL != pPlaceholder, E_POINTER );
|
|
|
|
//
|
|
// Get the pointers setup correctly to this buffer because the
|
|
// type REPARSE_DATA_BUFFER actually doesn't have any space
|
|
// allocated for the data and that is our own type, so get pointers
|
|
// pointing into the real allocated space so we can use them
|
|
//
|
|
pHsmData = (PRP_DATA) &pReparseBuffer->GenericReparseBuffer.DataBuffer[0];
|
|
|
|
//
|
|
// Validate the key public fields to make sure it is data we
|
|
// understand
|
|
//
|
|
WsbAffirm( IO_REPARSE_TAG_HSM == pReparseBuffer->ReparseTag , S_FALSE );
|
|
WsbAffirm( sizeof(RP_DATA) == pReparseBuffer->ReparseDataLength , S_FALSE );
|
|
|
|
//
|
|
// Copy over the RP_DATA information
|
|
//
|
|
WsbAffirmHr(CopyRPDataToPlaceholder(pHsmData, pPlaceholder));
|
|
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CopyRPToPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return( hr );
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::CalculateCurrentCRCAndUSN(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size,
|
|
OUT ULONG *pCurrentCRC,
|
|
OUT LONGLONG *pUsn
|
|
)
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrTest = S_OK;
|
|
CWsbStringPtr path;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
FILE_BASIC_INFORMATION basicInformation;
|
|
|
|
try {
|
|
WsbTraceIn(OLESTR("CFsaScanItem::CalculateCurrentCRCAndUSN"), OLESTR("offset = <%I64d>, size = <%I64d>"),
|
|
offset, size);
|
|
|
|
//
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
//WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
|
|
WsbTrace(OLESTR("CFsaScanItem::CalculateCurrentCRCAndUSN for file <%ls>"), (OLECHAR *)path);
|
|
|
|
// Open the file.
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE,
|
|
FILE_READ_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES,
|
|
EXCLUSIVE_FLAG,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&handle ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( handle );
|
|
|
|
//
|
|
// Get the current attributes of the file and the times
|
|
//
|
|
WsbAssertNtStatus( NtQueryInformationFile( handle,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
//
|
|
// Set the time flags so that when we close the handle the
|
|
// time are not updated on the file and the FileAttributes
|
|
//
|
|
basicInformation.CreationTime.QuadPart = -1;
|
|
basicInformation.LastAccessTime.QuadPart = -1;
|
|
basicInformation.LastWriteTime.QuadPart = -1;
|
|
basicInformation.ChangeTime.QuadPart = -1;
|
|
WsbAssertNtStatus( NtSetInformationFile( handle,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
//
|
|
// Calculate the CRC
|
|
//
|
|
WsbAffirmHr(CalculateCurrentCRCInternal(handle, offset, size, pCurrentCRC));
|
|
|
|
//
|
|
// Calculate the USN
|
|
//
|
|
*pUsn = 0;
|
|
hr = WsbGetUsnFromFileHandle(handle, FALSE, pUsn);
|
|
if (S_OK != hr) {
|
|
//
|
|
// If we can't get the USN set it to 0 which is an invalid
|
|
// USN and keep going.
|
|
*pUsn = 0;
|
|
hr = S_OK;
|
|
}
|
|
|
|
//
|
|
// Close the file
|
|
//
|
|
NtClose( handle );
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
} WsbCatch( hr );
|
|
|
|
//
|
|
// Close the file for sure
|
|
//
|
|
if( INVALID_HANDLE_VALUE != handle) {
|
|
NtClose( handle );
|
|
}
|
|
|
|
WsbTraceOut(OLESTR("CalculateCurrentCRCAndUSN"), OLESTR("hr = <%ls>, CRC is <%ls>, USN is <%ls>"),
|
|
WsbHrAsString(hr), WsbPtrToUlongAsString(pCurrentCRC), WsbPtrToLonglongAsString(pUsn));
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT
|
|
CFsaScanItem::CalculateCurrentCRCInternal(
|
|
IN HANDLE handle,
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size,
|
|
ULONG *pCurrentCRC
|
|
)
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrTest = S_OK;
|
|
register ULONG crc32 = 0;
|
|
LONGLONG bytesRemaining;
|
|
LONGLONG bytesToRead;
|
|
ULONG bufferSize;
|
|
ULONG bytesRead;
|
|
CHAR * pBuffer = 0;
|
|
CHAR * pCurrent;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
try {
|
|
WsbTraceIn(OLESTR("CFsaScanItem::CalculateCurrentCRCInternal"), OLESTR("offset = <%I64d>, size = <%I64d>"),
|
|
offset, size);
|
|
|
|
|
|
// set initial value of CRC to 'pre-conditioning value'
|
|
INITIALIZE_CRC(crc32);
|
|
|
|
//
|
|
// Set up to read where we want to start
|
|
//
|
|
LARGE_INTEGER startPoint;
|
|
startPoint.QuadPart = offset;
|
|
|
|
// Get the size of the file.
|
|
bytesToRead = size;
|
|
|
|
//
|
|
// Figure out the size of the buffer to create
|
|
//
|
|
if (bytesToRead < 1024*1024) {
|
|
//
|
|
// Allocate one buffer the size of the file
|
|
//
|
|
bufferSize = (ULONG)bytesToRead;
|
|
} else {
|
|
bufferSize = (1024 * 1024);
|
|
}
|
|
|
|
pBuffer = (CHAR *)malloc(bufferSize);
|
|
if (0 == pBuffer) {
|
|
//
|
|
// Try again for half the space
|
|
//
|
|
bufferSize = bufferSize / 2;
|
|
pBuffer = (CHAR *)malloc(bufferSize);
|
|
if (0 == pBuffer) {
|
|
WsbThrow( E_OUTOFMEMORY );
|
|
}
|
|
}
|
|
|
|
// Start calculating CRC by processing a 'chunk' of the file at a time.
|
|
// While there are still chunks left, read that amount. Otherwise read the amount left.
|
|
for (bytesRemaining = bytesToRead; bytesRemaining > 0; bytesRemaining -= bytesRead) {
|
|
|
|
// Read data from the file.
|
|
WsbAssertNtStatus(NtReadFile(handle, NULL, NULL, NULL, &IoStatusBlock, pBuffer, bufferSize, &startPoint, NULL));
|
|
bytesRead = (DWORD)IoStatusBlock.Information;
|
|
startPoint.QuadPart += bytesRead;
|
|
|
|
// Each byte needs to be added into the CRC.
|
|
for (pCurrent = pBuffer; (pCurrent < (pBuffer + bytesRead)) && (S_OK == hr); pCurrent++) {
|
|
|
|
hrTest = WsbCRCReadFile((UCHAR *)pCurrent, &crc32);
|
|
if (S_OK != hrTest) {
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// return ones-complement of the calc'd CRC value - this is the actual CRC
|
|
FINIALIZE_CRC(crc32);
|
|
*pCurrentCRC = crc32;
|
|
|
|
} WsbCatch( hr );
|
|
|
|
//
|
|
// Make sure allocated memory is freed
|
|
//
|
|
if (0 != pBuffer) {
|
|
free(pBuffer);
|
|
}
|
|
|
|
|
|
WsbTraceOut(OLESTR("CalculateCurrentCRCInternal"), OLESTR("hr = <%ls>, CRC is <%ls>"), WsbHrAsString(hr), WsbPtrToUlongAsString(pCurrentCRC));
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT
|
|
CFsaScanItem::CreatePlaceholder(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size,
|
|
IN FSA_PLACEHOLDER placeholder,
|
|
IN BOOL checkUsn,
|
|
IN LONGLONG usn,
|
|
OUT LONGLONG *pUsn
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::CreatePlaceholder().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CWsbStringPtr path;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
ULONG DesiredAccess;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PREPARSE_DATA_BUFFER pReparseBuffer;
|
|
UCHAR ReparseBuffer[sizeof(REPARSE_DATA_BUFFER) + sizeof(RP_DATA) + 10];
|
|
NTSTATUS ntStatus;
|
|
FILE_BASIC_INFORMATION basicInformation;
|
|
LONGLONG lastWriteTime;
|
|
LONGLONG nowUsn = 0;
|
|
CWsbStringPtr volName;
|
|
ULONG attributes;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::CreatePlaceholder"), OLESTR("offset = <%I64d>, size = <%I64d>, checkUsn = <%ls>, usn = <%I64d>"),
|
|
offset, size, WsbBoolAsString(checkUsn), usn);
|
|
try {
|
|
BOOL wasReadOnly = FALSE;
|
|
|
|
//
|
|
// Set the offset and size information
|
|
//
|
|
placeholder.dataStreamStart = offset;
|
|
placeholder.dataStreamSize = size;
|
|
|
|
//
|
|
// Get the pointers setup correctly to this buffer because the
|
|
// type REPARSE_DATA_BUFFER actually doesn't have any space
|
|
// allocated for the data and that is our own type, so get pointers
|
|
// pointing into the real allocated space so we can use them
|
|
//
|
|
pReparseBuffer = (PREPARSE_DATA_BUFFER)ReparseBuffer;
|
|
WsbAffirmHr( CopyPlaceholderToRP( &placeholder, pReparseBuffer, placeholder.isTruncated ) );
|
|
|
|
//
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
//WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
|
|
|
|
// Save whether this was readonly for later
|
|
if (S_OK == IsReadOnly()) {
|
|
wasReadOnly = TRUE;
|
|
}
|
|
|
|
//
|
|
// Make sure the file is read/write
|
|
WsbAffirmHr( MakeReadWrite() );
|
|
|
|
//
|
|
// Open the file to put the placeholder information in the reparse point
|
|
//
|
|
DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES ;
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
|
|
DesiredAccess,
|
|
EXCLUSIVE_FLAG,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&handle ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( handle );
|
|
|
|
//
|
|
// Tell the USN journal that we are the source of the changes.
|
|
//
|
|
WsbAffirmHr(m_pResource->GetPath(&volName, 0));
|
|
WsbAffirmHr(WsbMarkUsnSource(handle, volName));
|
|
|
|
|
|
//
|
|
// Get the USN from the file now before any writes occur.
|
|
// Note: NtSetInformationFile will not change the USN if you set the FileAttributes to 0
|
|
// and the dates to -1. Setting the attributes to 0 leaves them unchanged.
|
|
//
|
|
// (For now we skip this check for read-only files because the call to MakeReadWrite
|
|
// changes the USN. This needs to be fixed in the future.)
|
|
//
|
|
if (checkUsn && !wasReadOnly) {
|
|
//
|
|
// Get the current USN for this file
|
|
//
|
|
hr = WsbGetUsnFromFileHandle(handle, FALSE, &nowUsn);
|
|
if (S_OK != hr) {
|
|
nowUsn = 0;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the current attributes of the file and the times
|
|
//
|
|
WsbAssertNtStatus( NtQueryInformationFile( handle,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
lastWriteTime = basicInformation.LastWriteTime.QuadPart;
|
|
|
|
//
|
|
// Set the time flags so that when we close the handle the
|
|
// time are not updated on the file and the FileAttributes
|
|
// indicate the file is offline. You must do this AFTER you
|
|
// get the USN because the NtSetInformationFile changes the USN
|
|
//
|
|
basicInformation.CreationTime.QuadPart = -1;
|
|
basicInformation.LastAccessTime.QuadPart = -1;
|
|
basicInformation.LastWriteTime.QuadPart = -1;
|
|
basicInformation.ChangeTime.QuadPart = -1;
|
|
//
|
|
// Set the attributes to 0 to avoid the usn change (the file attributes will remain unchanged).
|
|
//
|
|
attributes = basicInformation.FileAttributes;
|
|
basicInformation.FileAttributes = 0; // No change to attributes
|
|
|
|
WsbAssertNtStatus( NtSetInformationFile( handle,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
|
|
basicInformation.FileAttributes = attributes;
|
|
|
|
//
|
|
// Make sure that the modify time of the file matches that
|
|
// of the placeholder data.
|
|
//
|
|
if (lastWriteTime != placeholder.fileVersionId) {
|
|
//
|
|
// The file has changed - don't put the reparse point on the file.
|
|
//
|
|
hr = FSA_E_REPARSE_NOT_WRITTEN_FILE_CHANGED;
|
|
WsbLogEvent(FSA_MESSAGE_REPARSE_NOT_WRITTEN_FILE_CHANGED, 0, NULL, WsbAbbreviatePath(path, 120), WsbHrAsString(hr), NULL);
|
|
WsbThrow( hr );
|
|
} else if (checkUsn) {
|
|
//
|
|
// If we are to check the USN do it now
|
|
//
|
|
|
|
//
|
|
// Rember if a USN is 0, it is not useful information so we can't
|
|
// rely on it
|
|
//
|
|
WsbTrace(OLESTR("CFsaScanItem::CreatePlaceholder premig usn = <%I64d>, current usn <%I64d>\n"),
|
|
usn, nowUsn);
|
|
if ((0 != nowUsn) && (0 != usn) && (nowUsn != usn)) {
|
|
//
|
|
// The file has changed - don't put the reparse point on the file.
|
|
//
|
|
hr = FSA_E_REPARSE_NOT_WRITTEN_FILE_CHANGED;
|
|
WsbLogEvent(FSA_MESSAGE_REPARSE_NOT_WRITTEN_FILE_CHANGED, 0, NULL, WsbAbbreviatePath(path, 120), WsbHrAsString(hr), NULL);
|
|
WsbThrow( hr );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make the file able to be a sparse file
|
|
// Note we assert only if the error is not a disk full error because we can get STATUS_NO_DISK_SPACE from this call and we
|
|
// do not want to see the log for that error.
|
|
// This is because the file must be padded out to a 16 cluster boundry before being made sparse.
|
|
//
|
|
// Note that this call does not affect the files data. It just enables "sparseness" for the file.
|
|
//
|
|
ntStatus = NtFsControlFile( handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
FSCTL_SET_SPARSE,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
if (STATUS_DISK_FULL == ntStatus) {
|
|
// Check whether the disk is really full, otherwise, assume that user has passed the quota limit
|
|
// Note: we ignore errors here and assume disk-full
|
|
hr = CheckIfDiskFull();
|
|
if (S_OK == hr) {
|
|
hr = FSA_E_REPARSE_NOT_CREATED_DISK_FULL;
|
|
} else if (S_FALSE == hr) {
|
|
hr = FSA_E_REPARSE_OWNER_PASS_QUOTA;
|
|
} else {
|
|
// error
|
|
WsbTraceAlways(OLESTR("CFsaScanItem::CreatePlaceholder: failed to check disk space after DISK_FULL error. hr=<%ls>\n"),
|
|
WsbHrAsString(hr));
|
|
hr = FSA_E_REPARSE_NOT_CREATED_DISK_FULL;
|
|
}
|
|
|
|
} else {
|
|
hr = HRESULT_FROM_NT(ntStatus);
|
|
}
|
|
WsbLogEvent(FSA_MESSAGE_REPARSE_NOT_CREATED, 0, NULL,
|
|
WsbAbbreviatePath(path, 120), WsbHrAsString(hr), NULL);
|
|
WsbThrow(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// Do the work of setting the Reparse Point
|
|
//
|
|
ntStatus = NtFsControlFile( handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
FSCTL_SET_REPARSE_POINT,
|
|
pReparseBuffer,
|
|
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
|
|
+ pReparseBuffer->ReparseDataLength,
|
|
NULL,
|
|
0 );
|
|
|
|
//
|
|
// Check the return code, if everything worked update the in memory flag
|
|
//
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
if (STATUS_DISK_FULL == ntStatus) {
|
|
// Check whether the disk is really full, otherwise, assume that user has passed the quota limit
|
|
// Note: we ignore errors here and assume disk-full
|
|
hr = CheckIfDiskFull();
|
|
if (S_OK == hr) {
|
|
hr = FSA_E_REPARSE_NOT_CREATED_DISK_FULL;
|
|
} else if (S_FALSE == hr) {
|
|
hr = FSA_E_REPARSE_OWNER_PASS_QUOTA;
|
|
} else {
|
|
// error
|
|
WsbTraceAlways(OLESTR("CFsaScanItem::CreatePlaceholder: failed to check disk space after DISK_FULL error. hr=<%ls>\n"),
|
|
WsbHrAsString(hr));
|
|
hr = FSA_E_REPARSE_NOT_CREATED_DISK_FULL;
|
|
}
|
|
|
|
} else {
|
|
hr = HRESULT_FROM_NT(ntStatus);
|
|
}
|
|
WsbLogEvent(FSA_MESSAGE_REPARSE_NOT_CREATED, 0, NULL,
|
|
WsbAbbreviatePath(path, 120), WsbHrAsString(hr), NULL);
|
|
WsbThrow(hr);
|
|
}
|
|
|
|
//
|
|
// Now that we change the bit, change the in memory flags for
|
|
// this scan item
|
|
//
|
|
m_findData.dwFileAttributes |= BIT_FOR_RP;
|
|
|
|
//
|
|
// Set the OFFLINE attribute to indicate the correct status of
|
|
// the file
|
|
//
|
|
if( placeholder.isTruncated ) {
|
|
basicInformation.FileAttributes |= BIT_FOR_TRUNCATED;
|
|
} else {
|
|
basicInformation.FileAttributes &= ~BIT_FOR_TRUNCATED;
|
|
}
|
|
|
|
basicInformation.FileAttributes |= FILE_ATTRIBUTE_NORMAL; // Just in case result was zero (then no attributes would be set)
|
|
|
|
WsbAssertNtStatus( NtSetInformationFile( handle,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
//
|
|
// Get the current attributes of the file and the times
|
|
//
|
|
WsbAssertNtStatus( NtQueryInformationFile( handle,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
//
|
|
// Set the in memory copy of the attributes to the right values
|
|
//
|
|
m_findData.dwFileAttributes = basicInformation.FileAttributes;
|
|
|
|
//
|
|
// Restore original attributes if required (must be done before retrieving the USN
|
|
// since changing attributes changes the USN as well)
|
|
//
|
|
if (TRUE == m_changedAttributes) {
|
|
RestoreAttributes();
|
|
}
|
|
|
|
//
|
|
// Before we close the file, get the USN to return to the caller
|
|
// Writing the reparse information will change the USN.
|
|
//
|
|
hr = WsbGetUsnFromFileHandle(handle, TRUE, pUsn);
|
|
if (S_OK != hr) {
|
|
*pUsn = 0;
|
|
hr = S_OK;
|
|
}
|
|
|
|
//
|
|
// Close the file since we are done with it and set the handle to invalid
|
|
//
|
|
WsbAssertNtStatus( NtClose( handle ) );
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Now that everything worked change the in memory flags for
|
|
// this scan item
|
|
//
|
|
m_placeholder = placeholder;
|
|
m_gotPlaceholder = TRUE;
|
|
WsbTrace( OLESTR("(CreatePlaceholder) Reparse CRC <%ls>\n"),
|
|
WsbLonglongAsString( m_placeholder.dataStreamCRC ) );
|
|
|
|
|
|
} WsbCatch(hr);
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != handle) {
|
|
NtClose( handle );
|
|
}
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::CreatePlaceholder"), OLESTR("hr = <%ls>, usn = <%ls>"),
|
|
WsbHrAsString(hr), WsbPtrToLonglongAsString(pUsn));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::DeletePlaceholder(
|
|
IN LONGLONG /*offset*/,
|
|
IN LONGLONG /*size*/
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::DeletePlaceholder().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CWsbStringPtr path;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
ULONG DesiredAccess;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PREPARSE_DATA_BUFFER pReparseBuffer;
|
|
UCHAR ReparseBuffer[sizeof(REPARSE_DATA_BUFFER) + sizeof(RP_DATA) + 10];
|
|
NTSTATUS ntStatus;
|
|
FILE_BASIC_INFORMATION basicInformation;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::DeletePlaceholder"), OLESTR(""));
|
|
//
|
|
// Remove the Reparse Point off the file
|
|
//
|
|
try {
|
|
|
|
//
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
//WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
|
|
|
|
// Make sure it is read/write
|
|
WsbAffirmHr( MakeReadWrite() );
|
|
//
|
|
// Open the file to remove the placeholder information in the reparse point
|
|
//
|
|
DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES ;
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
|
|
DesiredAccess,
|
|
EXCLUSIVE_FLAG,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&handle ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( handle );
|
|
|
|
//
|
|
// Get the current attributes of the file and the times
|
|
//
|
|
WsbAssertNtStatus( NtQueryInformationFile( handle,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
//
|
|
// Set the time flags so that when we close the handle the
|
|
// time are not updated on the file and the FileAttributes
|
|
// indicate the file is offline
|
|
//
|
|
basicInformation.CreationTime.QuadPart = -1;
|
|
basicInformation.LastAccessTime.QuadPart = -1;
|
|
basicInformation.LastWriteTime.QuadPart = -1;
|
|
basicInformation.ChangeTime.QuadPart = -1;
|
|
basicInformation.FileAttributes &= ~BIT_FOR_TRUNCATED;
|
|
basicInformation.FileAttributes |= FILE_ATTRIBUTE_NORMAL; // Just in case result was zero (then no attributes would be set)
|
|
|
|
WsbAssertNtStatus( NtSetInformationFile( handle,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
m_findData.dwFileAttributes &= ~BIT_FOR_TRUNCATED;
|
|
m_originalAttributes &= ~BIT_FOR_TRUNCATED;
|
|
|
|
//
|
|
// Get the pointers setup correctly to this buffer because the
|
|
// type REPARSE_DATA_BUFFER actually doesn't have any space
|
|
// allocated for the data and that is our own type, so get pointers
|
|
// pointing into the real allocated space so we can use them
|
|
//
|
|
pReparseBuffer = (PREPARSE_DATA_BUFFER)ReparseBuffer;
|
|
|
|
|
|
pReparseBuffer->ReparseTag = IO_REPARSE_TAG_HSM ;
|
|
pReparseBuffer->ReparseDataLength = 0 ;
|
|
pReparseBuffer->Reserved = 0 ;
|
|
|
|
//
|
|
// Do the work of deleting the Reparse Point
|
|
//
|
|
ntStatus = NtFsControlFile( handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
FSCTL_DELETE_REPARSE_POINT,
|
|
pReparseBuffer,
|
|
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer),
|
|
NULL,
|
|
0 );
|
|
|
|
//
|
|
// Check the return code - verify this is the correct way to check
|
|
//
|
|
WsbAssertNtStatus( ntStatus );
|
|
|
|
//
|
|
// Close the file since we are done with it and set the handle to invalid
|
|
//
|
|
WsbAssertNtStatus( NtClose( handle ) );
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
//
|
|
// Now that everything worked change the in memory flags for
|
|
// this scan item
|
|
//
|
|
m_findData.dwFileAttributes &= ~BIT_FOR_RP;
|
|
m_gotPlaceholder = FALSE;
|
|
|
|
} WsbCatch(hr);
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != handle) {
|
|
NtClose( handle );
|
|
}
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::DeletePlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::GetFromRPIndex(
|
|
BOOL first
|
|
)
|
|
|
|
/*
|
|
|
|
Get file information from the Reparse Point Index
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOLEAN bFirst;
|
|
|
|
bFirst = (BOOLEAN)( first ? TRUE : FALSE );
|
|
WsbTraceIn(OLESTR("CFsaScanItem::GetFromRPIndex"), OLESTR(""));
|
|
|
|
try {
|
|
HRESULT hrFindFileId;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
IFsaScanItem * pScanItem;
|
|
FILE_REPARSE_POINT_INFORMATION ReparsePointInfo;
|
|
NTSTATUS Status;
|
|
|
|
WsbAssert(0 != m_handleRPI, E_FAIL);
|
|
|
|
try_again:
|
|
Status = NtQueryDirectoryFile(m_handleRPI,
|
|
NULL, // Event
|
|
NULL, // ApcRoutine
|
|
NULL, // ApcContext
|
|
&IoStatusBlock,
|
|
&ReparsePointInfo,
|
|
sizeof(ReparsePointInfo),
|
|
FileReparsePointInformation,
|
|
TRUE, // ReturnSingleEntry
|
|
NULL, // FileName
|
|
bFirst ); // RestartScan
|
|
if (Status != STATUS_SUCCESS) {
|
|
WsbTrace(OLESTR("CFsaScanItem::GetFromRPIndex: CreateFileW failed, GetLastError = %ld\n"),
|
|
GetLastError());
|
|
WsbThrow(WSB_E_NOTFOUND);
|
|
}
|
|
|
|
// Reset some items in case this isn't the first call to
|
|
// FindFileId
|
|
if (INVALID_HANDLE_VALUE != m_handle) {
|
|
FindClose(m_handle);
|
|
m_handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
if (TRUE == m_changedAttributes) {
|
|
RestoreAttributes();
|
|
}
|
|
|
|
// Find the file from the ID (not efficient or elegant, perhaps, but
|
|
// the code is already there).
|
|
pScanItem = this;
|
|
hrFindFileId = m_pResource->FindFileId(ReparsePointInfo.FileReference,
|
|
m_pSession, &pScanItem);
|
|
|
|
// If the FindFileId failed, we just skip that item and get the
|
|
// next one. This is to keep the scan from just stopping on this
|
|
// item. FindFileId could fail because the file has been deleted
|
|
// already or the NT code could have a bug that prevents finding
|
|
// the file name from the ID when the ID ends with 0x5C.
|
|
if (!SUCCEEDED(hrFindFileId)) {
|
|
bFirst = FALSE;
|
|
goto try_again;
|
|
}
|
|
WsbAffirmHr(pScanItem->Release()); // Get rid of extra ref. count
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::GetFromRPIndex"),
|
|
OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::CheckUsnJournalForChanges(
|
|
LONGLONG StartUsn,
|
|
LONGLONG StopUsn,
|
|
BOOL* pChanged
|
|
)
|
|
|
|
/*
|
|
|
|
Check the USN Journal for changes to the unnamed data stream for this
|
|
file between the given USNs.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::CheckUsnJournalForChanges"), OLESTR(""));
|
|
|
|
*pChanged = FALSE;
|
|
try {
|
|
LONGLONG fileId;
|
|
CWsbStringPtr volName;
|
|
ULONGLONG volUsnJournalId;
|
|
|
|
WsbAffirm(StartUsn <= StopUsn, E_UNEXPECTED);
|
|
WsbAffirmHr(m_pResource->GetPath(&volName, 0));
|
|
WsbAffirmHr(m_pResource->GetUsnId(&volUsnJournalId));
|
|
WsbAffirmHr(GetFileId(&fileId));
|
|
WsbAffirmHr(WsbCheckUsnJournalForChanges(volName, volUsnJournalId, fileId,
|
|
StartUsn, StopUsn, pChanged));
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::CheckUsnJournalForChanges"),
|
|
OLESTR("changed = %ls, hr = <%ls>"), WsbBoolAsString(*pChanged),
|
|
WsbHrAsString(hr));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::FindFirstInRPIndex(
|
|
IN IFsaResource* pResource,
|
|
IN IHsmSession* pSession
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaResource::FindFirstInRPIndex
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::FindFirstInRPIndex"), OLESTR(""));
|
|
|
|
try {
|
|
CWsbStringPtr path;
|
|
|
|
WsbAssert(0 != pResource, E_POINTER);
|
|
|
|
// Store off some of the scan information.
|
|
m_pResource = pResource;
|
|
m_pSession = pSession;
|
|
|
|
// Generate the Reparse Point Index directory name for this volume
|
|
WsbAffirmHr(pResource->GetPath(&path, 0));
|
|
WsbAffirmHr(path.Prepend("\\\\?\\"));
|
|
WsbAffirmHr(path.Append("$Extend\\$Reparse:$R:$INDEX_ALLOCATION"));
|
|
|
|
WsbTrace(OLESTR("CFsaScanItem::FindFirstInRPIndex: path = <%ls>\n"),
|
|
static_cast<WCHAR*>(path));
|
|
|
|
// Open the Reparse Point Index
|
|
m_handleRPI = CreateFileW(static_cast<WCHAR*>(path),
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS | SECURITY_IMPERSONATION,
|
|
NULL );
|
|
if (INVALID_HANDLE_VALUE == m_handleRPI) {
|
|
WsbTrace(OLESTR("CFsaScanItem::FindFirstInRPIndex: CreateFileW failed, GetLastError = %ld\n"),
|
|
GetLastError());
|
|
WsbThrow(WSB_E_NOTFOUND);
|
|
}
|
|
|
|
// Get file information
|
|
WsbAffirmHr(GetFromRPIndex(TRUE));
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::FindFirstInRPIndex"),
|
|
OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::FindFirstPlaceholder(
|
|
IN OUT LONGLONG* pOffset,
|
|
IN OUT LONGLONG* pSize,
|
|
IN OUT FSA_PLACEHOLDER* pPlaceholder
|
|
)
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::FindFirstPlaceholder().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::FindFirstPlaceholder"), OLESTR(""));
|
|
try {
|
|
|
|
WsbAssert(0 != pOffset, E_POINTER);
|
|
WsbAssert(0 != pSize, E_POINTER);
|
|
WsbAssert(0 != pPlaceholder, E_POINTER);
|
|
|
|
// Until these routines get rewritten, assume that the first placeholder is the one for the
|
|
// who file that is returned by GetPlaceholder().
|
|
*pOffset = 0;
|
|
WsbAffirmHr(GetLogicalSize(pSize));
|
|
|
|
// The code above assumes that a WSB_E_NOTFOUND error will be returned if there is no
|
|
// reparse point.
|
|
try {
|
|
WsbAffirmHr(GetPlaceholder(*pOffset, *pSize, pPlaceholder));
|
|
} WsbCatchAndDo(hr, if (E_UNEXPECTED == hr) {hr = WSB_E_NOTFOUND;});
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::FindFirstPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::FindNextInRPIndex(
|
|
void
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaResource::FindNextInRPIndex
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::FindNextInRPIndex"), OLESTR(""));
|
|
|
|
try {
|
|
|
|
WsbAssert(0 != m_handleRPI, E_FAIL);
|
|
|
|
// Get file information
|
|
WsbAffirmHr(GetFromRPIndex(FALSE));
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::FindNextInRPIndex"),
|
|
OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::FindNextPlaceholder(
|
|
IN OUT LONGLONG* pOffset,
|
|
IN OUT LONGLONG* pSize,
|
|
IN OUT FSA_PLACEHOLDER* pPlaceholder
|
|
)
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::FindNextPlaceholder().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::FindNext"), OLESTR(""));
|
|
try {
|
|
|
|
WsbAssert(0 != pOffset, E_POINTER);
|
|
WsbAssert(0 != pSize, E_POINTER);
|
|
WsbAssert(0 != pPlaceholder, E_POINTER);
|
|
|
|
// Until these routines get rewritten, assume there is only one placeholder.
|
|
hr = WSB_E_NOTFOUND;
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::FindNextPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::GetFileId(
|
|
OUT LONGLONG* pFileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::GetFileId().
|
|
|
|
--*/
|
|
{
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
HRESULT hr = S_OK;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::GetFileId"), OLESTR(""));
|
|
|
|
try {
|
|
ULONG DesiredAccess;
|
|
FILE_INTERNAL_INFORMATION iInfo;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
CWsbStringPtr path;
|
|
|
|
WsbAssert(0 != pFileId, E_POINTER);
|
|
|
|
//
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
//WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
|
|
WsbTrace(OLESTR("CFsaScanItem::GetFileId, full Path = <%ls>\n"),
|
|
static_cast<WCHAR*>(path));
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
DesiredAccess = FILE_READ_ATTRIBUTES ;
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE,
|
|
DesiredAccess,
|
|
SHARE_FLAGS,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&handle ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( handle );
|
|
|
|
// Get the internal information
|
|
WsbAssertNtStatus( NtQueryInformationFile( handle,
|
|
&IoStatusBlock,
|
|
&iInfo,
|
|
sizeof(FILE_INTERNAL_INFORMATION),
|
|
FileInternalInformation ));
|
|
|
|
// Get the file id
|
|
*pFileId = iInfo.IndexNumber.QuadPart;
|
|
|
|
//
|
|
// Close the file since we are done with it and set the handle to invalid
|
|
//
|
|
WsbAssertNtStatus( NtClose( handle ) );
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
} WsbCatchAndDo(hr,
|
|
WsbTrace(OLESTR("CFsaScanItem::GetFileId, GetLastError = %lx\n"),
|
|
GetLastError());
|
|
);
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != handle) {
|
|
NtClose( handle );
|
|
}
|
|
WsbTraceOut(OLESTR("CFsaScanItem::GetFileId"), OLESTR("Hr = <%ls>, FileId = %I64x"),
|
|
WsbHrAsString(hr), *pFileId);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::GetFileUsn(
|
|
OUT LONGLONG* pFileUsn
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the current USN Journal number for this file.
|
|
|
|
Arguments:
|
|
|
|
pFileUsn - Pointer to File USN to be returned.
|
|
|
|
Return Value:
|
|
|
|
S_OK - success
|
|
|
|
--*/
|
|
{
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
HRESULT hr = S_OK;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::GetFileUsn"), OLESTR(""));
|
|
|
|
try {
|
|
ULONG DesiredAccess;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
CWsbStringPtr path;
|
|
|
|
WsbAssert(pFileUsn, E_POINTER);
|
|
|
|
//
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
WsbTrace(OLESTR("CFsaScanItem::GetFileUsn, full Path = <%ls>\n"),
|
|
static_cast<WCHAR*>(path));
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
DesiredAccess = FILE_READ_ATTRIBUTES ;
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
|
|
DesiredAccess,
|
|
SHARE_FLAGS,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&handle ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( handle );
|
|
|
|
// Get the internal information
|
|
WsbAffirmHr(WsbGetUsnFromFileHandle(handle, FALSE, pFileUsn));
|
|
|
|
//
|
|
// Close the file since we are done with it and set the handle to invalid
|
|
//
|
|
WsbAssertNtStatus( NtClose( handle ) );
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
} WsbCatchAndDo(hr,
|
|
WsbTrace(OLESTR("CFsaScanItem::GetFileUsn, GetLastError = %lx\n"),
|
|
GetLastError());
|
|
);
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != handle) {
|
|
NtClose( handle );
|
|
}
|
|
WsbTraceOut(OLESTR("CFsaScanItem::GetFileUsn"), OLESTR("Hr = <%ls>, FileUsn = %I64d"),
|
|
WsbHrAsString(hr), *pFileUsn);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::GetPlaceholder(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size,
|
|
OUT FSA_PLACEHOLDER* pPlaceholder
|
|
)
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::GetPlaceholder().
|
|
|
|
--*/
|
|
{
|
|
WsbTraceIn(OLESTR("CFsaScanItem::GetPlaceholder"), OLESTR(""));
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// If we already have the placeholder information just return it
|
|
//
|
|
try {
|
|
|
|
//
|
|
// Validate the file is managed. If it is the affirm will succeed.
|
|
// If the file is not managed then we can only tell the caller the
|
|
// problem.
|
|
//
|
|
WsbAffirmHr(hr = IsManaged(offset, size));
|
|
|
|
//
|
|
// Make sure the file is managed - will return S_OK
|
|
//
|
|
WsbAffirm( S_OK == hr, FSA_E_NOTMANAGED );
|
|
|
|
//
|
|
// Assert that the internal flag for the data is set, should
|
|
// always be on if the hr was S_OK above
|
|
//
|
|
WsbAssert( m_gotPlaceholder, E_UNEXPECTED );
|
|
|
|
//
|
|
// Copy the data to the callers structure
|
|
//
|
|
*pPlaceholder = m_placeholder;
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::GetPlaceholder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::HasExtendedAttributes(
|
|
void
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::HasExtendedAttributes().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
CWsbStringPtr path;
|
|
ULONG desiredAccess;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
FILE_EA_INFORMATION eaInformation;
|
|
|
|
try {
|
|
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
WsbAffirmHr(GetFullPathAndName(OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
//WsbAffirmHr(GetFullPathAndName(NULL, NULL, &path, 0));
|
|
|
|
// Open the file to get the attributes
|
|
desiredAccess = FILE_READ_ATTRIBUTES;
|
|
WsbAffirmHr(OpenObject(path, FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING, desiredAccess, SHARE_FLAGS,
|
|
FILE_OPEN, &ioStatusBlock, &handle));
|
|
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
WsbAssertHandle(handle);
|
|
|
|
// Get the current attributes of the file.
|
|
WsbAssertNtStatus(NtQueryInformationFile(handle, &ioStatusBlock, (VOID*) &eaInformation, sizeof(eaInformation ), FileEaInformation));
|
|
|
|
// Close the file since we are done with it and set the handle to invalid
|
|
WsbAssertNtStatus(NtClose(handle));
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
// Are there any EAs present?
|
|
if (eaInformation.EaSize != 0) {
|
|
hr = S_OK;
|
|
}
|
|
|
|
} WsbCatch(hr);
|
|
|
|
// if we opened the file we need to close it
|
|
if (INVALID_HANDLE_VALUE != handle) {
|
|
NtClose(handle);
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::IsALink(
|
|
void
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::IsALink().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
LONGLONG size;
|
|
|
|
//
|
|
// The file is a link if it is a reparse point and it is not our
|
|
// type.
|
|
//
|
|
|
|
WsbAffirmHr(GetLogicalSize(&size));
|
|
if (((m_findData.dwFileAttributes & BIT_FOR_RP) != 0) &&
|
|
(!(IsManaged(0, size) == S_OK))) {
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::IsManaged(
|
|
IN LONGLONG /*offset*/,
|
|
IN LONGLONG /*size*/
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::IsManaged().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
CWsbStringPtr path;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UCHAR ReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
|
NTSTATUS ntStatus;
|
|
ULONG DesiredAccess;
|
|
BOOL actualOfflineStatus = FALSE;
|
|
BOOL readReparseData = FALSE; // Used to know if we got an error reading the reparse data
|
|
BOOL changeOfflineStatus = FALSE;
|
|
FILE_BASIC_INFORMATION basicInformation;
|
|
CWsbStringPtr volName;
|
|
HRESULT saveHr;
|
|
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::IsManaged"), OLESTR(""));
|
|
//
|
|
// If the file has a reparse point then we need to get the information
|
|
// so we can tell if it is our type. Whether it is premigrated or
|
|
// truncate is not for this function to care, if it is either then
|
|
// the return is S_OK.
|
|
//
|
|
|
|
//
|
|
// If we already know we manage this file and have the placeholder
|
|
// information then tell caller
|
|
//
|
|
if ( m_gotPlaceholder) {
|
|
hr = S_OK;
|
|
actualOfflineStatus = m_placeholder.isTruncated;
|
|
readReparseData = TRUE;
|
|
|
|
//
|
|
// We don't know the answer so lets first check the reparse point bit.
|
|
// If it is not set then this is not managed by us
|
|
//
|
|
} else if ( (m_findData.dwFileAttributes & BIT_FOR_RP) == 0) {
|
|
hr = S_FALSE;
|
|
actualOfflineStatus = FALSE;
|
|
readReparseData = TRUE;
|
|
|
|
//
|
|
// So we know it has a reparse point but do not know what kind so
|
|
// lets get the data and fill in our global if we need
|
|
//
|
|
} else {
|
|
|
|
try {
|
|
//
|
|
// If the reparse point is not our type we get out now. This avoids a problem with SIS keeping
|
|
// the backing file open when one of their link files is open. Once we open the link file the backing file is
|
|
// opened by their filter and held open. If we attempt to migrate it later we get an error because it is open exclusive.
|
|
// This bit of code prevents us from being the one to trigger this condition - there is nothing we can do if some other
|
|
// process caused it to happen.
|
|
//
|
|
|
|
if (m_findData.dwReserved0 != IO_REPARSE_TAG_HSM) {
|
|
readReparseData = TRUE;
|
|
WsbThrow(S_FALSE);
|
|
}
|
|
|
|
//
|
|
// Create the real file name we need to open, under the
|
|
// covers this allocates the buffer since the path pointer
|
|
// is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
//WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
|
|
|
|
//
|
|
// Open the file to read the placeholder information in the reparse point
|
|
//
|
|
//DesiredAccess = FILE_READ_DATA | FILE_READ_ATTRIBUTES ;
|
|
DesiredAccess = FILE_READ_ATTRIBUTES ;
|
|
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
|
|
DesiredAccess,
|
|
SHARE_FLAGS,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&handle ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( handle );
|
|
|
|
//
|
|
// Read the placeholder information
|
|
//
|
|
ntStatus = NtFsControlFile( handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
FSCTL_GET_REPARSE_POINT,
|
|
NULL,
|
|
0,
|
|
&ReparseBuffer,
|
|
sizeof( ReparseBuffer ) );
|
|
|
|
|
|
//
|
|
// Verify that the get really worked. NOTE: If the reparse
|
|
// point is not there, it could be that it has been deleted since
|
|
// we last got the bits. We should just indicate that the file
|
|
// is not managed.
|
|
//
|
|
if (STATUS_NOT_A_REPARSE_POINT == ntStatus) {
|
|
readReparseData = TRUE;
|
|
WsbThrow(S_FALSE);
|
|
}
|
|
WsbAssertNtStatus( ntStatus );
|
|
|
|
//
|
|
// Close the file since we are done with it
|
|
//
|
|
WsbAssertNtStatus( NtClose( handle ) );
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
readReparseData = TRUE;
|
|
|
|
//
|
|
// Get the pointers setup correctly to this buffer because the
|
|
// type REPARSE_DATA_BUFFER actually doesn't have any space
|
|
// allocated for the data and that is our own type, so get pointers
|
|
// pointing into the real allocated space so we can use them
|
|
//
|
|
WsbAffirmHrOk( CopyRPToPlaceholder( (PREPARSE_DATA_BUFFER)ReparseBuffer, &m_placeholder ) );
|
|
|
|
actualOfflineStatus = m_placeholder.isTruncated;
|
|
|
|
//
|
|
// Set flag indicating placeholder found and information in memory
|
|
//
|
|
m_gotPlaceholder = TRUE;
|
|
hr = S_OK;
|
|
|
|
|
|
} WsbCatch(hr);
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != handle) {
|
|
NtClose( handle );
|
|
}
|
|
}
|
|
|
|
saveHr = hr;
|
|
|
|
// Check the actual offline status against the offline bit and fix it if necessary.
|
|
if (readReparseData) { // If there was no error getting the reparse data
|
|
|
|
WsbTrace(OLESTR("CFsaScanItem::IsManaged: Checking offline status %x - actual = %x\n"),
|
|
m_findData.dwFileAttributes & BIT_FOR_TRUNCATED, actualOfflineStatus );
|
|
|
|
switch (actualOfflineStatus) {
|
|
case TRUE:
|
|
if (!(m_findData.dwFileAttributes & BIT_FOR_TRUNCATED)) {
|
|
// Offline bit is not set and should be - set it.
|
|
m_findData.dwFileAttributes |= BIT_FOR_TRUNCATED;
|
|
m_originalAttributes |= BIT_FOR_TRUNCATED; // Just in case we have changed to read/write;
|
|
changeOfflineStatus = TRUE;
|
|
}
|
|
break;
|
|
case FALSE:
|
|
if (m_findData.dwFileAttributes & BIT_FOR_TRUNCATED) {
|
|
// Offline bit is set and should not be - clear it.
|
|
m_findData.dwFileAttributes &= ~BIT_FOR_TRUNCATED;
|
|
m_originalAttributes &= ~BIT_FOR_TRUNCATED; // Just in case we have changed to read/write;
|
|
changeOfflineStatus = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (changeOfflineStatus) {
|
|
// Set the new attribute
|
|
WsbTrace(OLESTR("CFsaScanItem::IsManaged: Changing offline status %x - actual = %x\n"),
|
|
m_findData.dwFileAttributes & BIT_FOR_TRUNCATED, actualOfflineStatus );
|
|
|
|
try {
|
|
//
|
|
// Create the real file name we need to open, under the
|
|
// covers this allocates the buffer since the path pointer
|
|
// is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
|
|
//
|
|
// Open the file to set attributes
|
|
//
|
|
DesiredAccess = FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
|
|
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
|
|
DesiredAccess,
|
|
SHARE_FLAGS,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&handle ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( handle );
|
|
|
|
WsbAffirmHr(m_pResource->GetPath(&volName, 0));
|
|
WsbAffirmHr(WsbMarkUsnSource(handle, volName));
|
|
|
|
// Set the time flags so that when we close the handle the
|
|
// time are not updated on the file and the FileAttributes
|
|
basicInformation.CreationTime.QuadPart = -1;
|
|
basicInformation.LastAccessTime.QuadPart = -1;
|
|
basicInformation.LastWriteTime.QuadPart = -1;
|
|
basicInformation.ChangeTime.QuadPart = -1;
|
|
basicInformation.FileAttributes = m_findData.dwFileAttributes;
|
|
|
|
WsbAffirmNtStatus(NtSetInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
|
|
|
|
//
|
|
// Close the file since we are done with it
|
|
//
|
|
WsbAssertNtStatus( NtClose( handle ) );
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
} WsbCatch(hr);
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != handle) {
|
|
NtClose( handle );
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = saveHr;
|
|
WsbTraceOut(OLESTR("CFsaScanItem::IsManaged"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::IsPremigrated(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::IsPremigrated().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
HRESULT hrTest = S_FALSE;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::IsPremigrated"), OLESTR(""));
|
|
// We really need to look at the placeholder information to figure
|
|
// this out (is offline, and is out type of HSM.
|
|
|
|
//
|
|
// If the file is NOT truncated AND is a reparse point and is a
|
|
// managed one then the file is a premigrated file
|
|
//
|
|
// if ( !(m_findData.dwFileAttributes & BIT_FOR_TRUNCATED) &&
|
|
// m_findData.dwFileAttributes & BIT_FOR_RP &&
|
|
// IsManaged() == S_OK) {
|
|
|
|
try {
|
|
|
|
if ( m_findData.dwFileAttributes & BIT_FOR_RP ) {
|
|
WsbAffirmHr(hrTest = IsManaged(offset, size));
|
|
if ((S_OK == hrTest) &&
|
|
( !m_placeholder.isTruncated )) {
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
} WsbCatch (hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::IsPremigrated"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::IsTruncated(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::IsTruncated().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
HRESULT hrTest = S_FALSE;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::IsTruncated"), OLESTR(""));
|
|
//
|
|
// If the bit is on that indicates we have truncated the file AND
|
|
// the file is a reparse point AND the reparse point is one of
|
|
// our types (i.e. it really is our information stuffed away in
|
|
// there the it really is a truncated file
|
|
//
|
|
// if ( // ???? m_findData.dwFileAttributes & BIT_FOR_TRUNCATED &&
|
|
// m_findData.dwFileAttributes & BIT_FOR_RP &&
|
|
// IsManaged() == S_OK && RP_FILE_IS_TRUNCATED( m_placeholder.bitFlags ) ) {
|
|
try {
|
|
|
|
if ( m_findData.dwFileAttributes & BIT_FOR_RP ) {
|
|
WsbAffirmHr(hrTest = IsManaged(offset, size));
|
|
if ((S_OK == hrTest) &&
|
|
( m_placeholder.isTruncated )) {
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
} WsbCatch (hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::IsTruncated"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::GetVersionId(
|
|
LONGLONG *fileVersionId
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::GetVersionId().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
CWsbStringPtr path;
|
|
ULONG DesiredAccess;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_BASIC_INFORMATION basicInformation;
|
|
|
|
try {
|
|
//
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
//WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
|
|
|
|
//
|
|
// Open the file to get the attributes
|
|
//
|
|
DesiredAccess = FILE_READ_ATTRIBUTES ;
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
|
|
DesiredAccess,
|
|
SHARE_FLAGS,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&handle ) );
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( handle );
|
|
|
|
//
|
|
// Get the current attributes of the file and the times
|
|
//
|
|
WsbAssertNtStatus( NtQueryInformationFile( handle,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
//
|
|
// Close the file since we are done with it and set the handle to invalid
|
|
//
|
|
WsbAssertNtStatus( NtClose( handle ) );
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
*fileVersionId = basicInformation.LastWriteTime.QuadPart;
|
|
hr = S_OK;
|
|
} WsbCatch( hr );
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != handle) {
|
|
NtClose( handle );
|
|
}
|
|
|
|
return( hr );
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::MakeReadWrite(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Make the file attributes read/write if they aren't already.
|
|
|
|
Arguments:
|
|
|
|
pUsn - Pointer to File USN to check (if != 0) and to be returned after the change.
|
|
|
|
Return Value:
|
|
|
|
S_OK - success
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CWsbStringPtr path;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
FILE_BASIC_INFORMATION basicInformation;
|
|
|
|
if (S_OK == IsReadOnly()) {
|
|
|
|
try {
|
|
|
|
// NOTE: MakeReadOnly(), IsReadOnly(), and RestoreAttributes() seem like dangerous implementations, since
|
|
// the used cached information and reset all the attirbutes. It is also assuming that the
|
|
// application wants the file reset to read only after FindNext() or the destructor. This
|
|
// may not be true for a general purpose application. Unfortunately, it seems to risky to
|
|
// try to change this implementation now.
|
|
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
WsbAffirmHr(GetFullPathAndName(OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
|
|
// Open the file.
|
|
WsbAffirmHr(OpenObject(path, FILE_NON_DIRECTORY_FILE, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, EXCLUSIVE_FLAG, FILE_OPEN, &IoStatusBlock, &handle));
|
|
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
WsbAffirmHandle(handle);
|
|
|
|
// Get the current attributes of the file and the times
|
|
WsbAffirmNtStatus(NtQueryInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
|
|
|
|
// Make sure it is still read only.
|
|
if ((basicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
|
|
|
|
m_originalAttributes = basicInformation.FileAttributes;
|
|
|
|
// Set the time flags so that when we close the handle the
|
|
// time are not updated on the file and the FileAttributes
|
|
basicInformation.CreationTime.QuadPart = -1;
|
|
basicInformation.LastAccessTime.QuadPart = -1;
|
|
basicInformation.LastWriteTime.QuadPart = -1;
|
|
basicInformation.ChangeTime.QuadPart = -1;
|
|
basicInformation.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
|
|
basicInformation.FileAttributes |= FILE_ATTRIBUTE_NORMAL; // Just in case result was zero (then no attributes would be set)
|
|
|
|
WsbAffirmNtStatus(NtSetInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
|
|
|
|
m_changedAttributes = TRUE;
|
|
}
|
|
|
|
// Close the file
|
|
NtClose(handle);
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
} WsbCatch(hr);
|
|
|
|
|
|
// Close the file for sure
|
|
if (INVALID_HANDLE_VALUE != handle) {
|
|
NtClose(handle);
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::PrepareForManage(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::PrepareForManage().
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER(offset);
|
|
UNREFERENCED_PARAMETER(size);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::RestoreAttributes(
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::RestoreAttributes
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CWsbStringPtr path;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
FILE_BASIC_INFORMATION basicInformation;
|
|
|
|
try {
|
|
|
|
// NOTE: MakeReadOnly(), IsReadOnly(), and RestoreAttributes() seem like dangerous implementations, since
|
|
// the used cached information and reset all the attirbutes. It is also assuming that the
|
|
// application wants the file reset to read only after FindNext() or the destructor. This
|
|
// may not be true for a general purpose application. Unfortunately, it seems to risky to
|
|
// try to change this implementation now.
|
|
|
|
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
WsbTrace(OLESTR("CFsaScanItem::RestoreAttributes - Restoring attributes to %x"), m_originalAttributes);
|
|
WsbAffirmHr(GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
|
|
|
|
// Open the file.
|
|
WsbAffirmHr(OpenObject(path, FILE_NON_DIRECTORY_FILE, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, EXCLUSIVE_FLAG, FILE_OPEN, &IoStatusBlock, &handle));
|
|
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
WsbAffirmHandle(handle);
|
|
|
|
// Get the current attributes of the file and the times
|
|
WsbAffirmNtStatus(NtQueryInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
|
|
|
|
// Set the time flags so that when we close the handle the
|
|
// time are not updated on the file and the FileAttributes
|
|
basicInformation.CreationTime.QuadPart = -1;
|
|
basicInformation.LastAccessTime.QuadPart = -1;
|
|
basicInformation.LastWriteTime.QuadPart = -1;
|
|
basicInformation.ChangeTime.QuadPart = -1;
|
|
basicInformation.FileAttributes = m_originalAttributes;
|
|
|
|
WsbAffirmNtStatus(NtSetInformationFile(handle, &IoStatusBlock, (PVOID)&basicInformation, sizeof(basicInformation), FileBasicInformation));
|
|
|
|
|
|
// Close the file
|
|
NtClose(handle);
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
m_changedAttributes = FALSE;
|
|
|
|
} WsbCatch(hr);
|
|
|
|
// Close the file for sure
|
|
if (INVALID_HANDLE_VALUE != handle) {
|
|
NtClose(handle);
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::Truncate(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::Truncate().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fileIsTruncated = FALSE;
|
|
LONGLONG usn = 0;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::Truncate"), OLESTR(""));
|
|
try {
|
|
|
|
// call the engine
|
|
if (IsManaged(offset, size) == S_OK) {
|
|
WsbAffirmHr(m_pResource->ValidateForTruncate((IFsaScanItem*) this, offset, size, usn));
|
|
}
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::Truncate"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::TruncateValidated(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::TruncateValidated().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HRESULT truncateHr = S_OK;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::TruncateValidated"), OLESTR(""));
|
|
try {
|
|
IFsaScanItem* pMe = this;
|
|
|
|
truncateHr = TruncateInternal(offset, size);
|
|
|
|
//
|
|
// Note: Must check for S_OK since TruncateInternal may return FSA_E_ITEMCHANGED or FSA_E_ITEMINUSE
|
|
// Both are "Success hr", but imply no truncation was done
|
|
//
|
|
if (S_OK == truncateHr) {
|
|
WsbAffirmHr(m_pResource->RemovePremigrated(pMe, offset, size));
|
|
WsbAffirmHr(m_pResource->AddTruncated(pMe, offset, size));
|
|
}
|
|
} WsbCatch(hr);
|
|
|
|
// The important hr to return to the caller is the actual result of the truncation
|
|
hr = truncateHr;
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::TruncateValidated"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::TruncateInternal(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::TruncateInternal().
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CWsbStringPtr path;
|
|
ULONG DesiredAccess;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS ntStatus;
|
|
FILE_END_OF_FILE_INFORMATION sizeInformation;
|
|
FILE_BASIC_INFORMATION basicInformation;
|
|
PREPARSE_DATA_BUFFER pReparseBuffer;
|
|
UCHAR ReparseBuffer[sizeof(REPARSE_DATA_BUFFER) + sizeof(RP_DATA) + 10];
|
|
CWsbStringPtr fileName;
|
|
CWsbStringPtr jobName;
|
|
LONGLONG fileVersionId;
|
|
ULONG i = 0;
|
|
CWsbStringPtr volName;
|
|
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::TruncateInternal"), OLESTR(""));
|
|
|
|
// Putting these statistics in the registry is probably not the best
|
|
// place for them, but it's the easiest solution for now
|
|
#define TEMPORARY_TRUNCATE_STATISTICS 1
|
|
#if defined(TEMPORARY_TRUNCATE_STATISTICS)
|
|
// Try to increment the truncate-attempt count in the registry
|
|
WsbIncRegistryValueDWORD(NULL, FSA_REGISTRY_PARMS,
|
|
OLESTR("TruncateCalls"));
|
|
#endif
|
|
|
|
// Get strings for tracing and error logging (ignore errors?!)
|
|
GetFullPathAndName( 0, 0, &fileName, 0);
|
|
m_pSession->GetName(&jobName, 0);
|
|
|
|
m_handleVerify = INVALID_HANDLE_VALUE;
|
|
|
|
try {
|
|
LONGLONG fileUsn1 = 0, fileUsn2 = 0;
|
|
|
|
// If the file is not migrated, then we can't truncate it
|
|
if (S_OK != IsPremigrated(offset, size)) {
|
|
if (S_OK != IsManaged(offset, size)) {
|
|
hr = FSA_E_NOTMANAGED;
|
|
WsbLogEvent(FSA_MESSAGE_TRUNCSKIPPED_ISNOTMANAGED, 0, NULL,
|
|
(OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120),
|
|
WsbHrAsString(hr), NULL);
|
|
WsbThrow(hr);
|
|
} else {
|
|
//
|
|
// Do not bother to log an event here as this should only
|
|
// happen if someone uses rstest or some other program
|
|
// to truncate a file that is already truncated.
|
|
WsbThrow(FSA_E_FILE_ALREADY_MANAGED);
|
|
}
|
|
}
|
|
|
|
WsbAssert( m_gotPlaceholder, E_UNEXPECTED );
|
|
|
|
//
|
|
// Setup the reparse point data with that which was on the file
|
|
// with the bit in the data indicating it is truncated
|
|
//
|
|
pReparseBuffer = (PREPARSE_DATA_BUFFER)ReparseBuffer;
|
|
WsbAffirmHr( CopyPlaceholderToRP( &m_placeholder, pReparseBuffer, TRUE ) );
|
|
|
|
//
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
|
|
//
|
|
// Open the file exclusively for read-only so we can get the usn before and after
|
|
// making the file R/W, without letting anybody to make a "real" change in the middle
|
|
//
|
|
DesiredAccess = FILE_READ_DATA | FILE_READ_ATTRIBUTES;
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE,
|
|
DesiredAccess,
|
|
EXCLUSIVE_FLAG,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&m_handleVerify ) );
|
|
|
|
WsbAssertHandle( m_handleVerify );
|
|
|
|
//
|
|
// Get usn before making R/W
|
|
// This usn is used to compare with the usn which we kept in the premigrated list.
|
|
// MakeReadWrite may chnage the usn so we need to get it before
|
|
//
|
|
if (S_OK != WsbGetUsnFromFileHandle(m_handleVerify, FALSE, &fileUsn1)) {
|
|
fileUsn1 = 0;
|
|
}
|
|
|
|
// Make sure it is read/write
|
|
WsbAffirmHr( MakeReadWrite() );
|
|
|
|
//
|
|
// Get usn after making R/W
|
|
// This usn will be use to compare with the usn of the file after we'll open it for R/W. We need
|
|
// this comparison in order to ensure that nobody changed the file before we opened it again for R/W.
|
|
//
|
|
if (S_OK != WsbGetUsnFromFileHandle(m_handleVerify, TRUE, &fileUsn2)) {
|
|
fileUsn2 = 0;
|
|
}
|
|
|
|
// Close the file
|
|
NtClose( m_handleVerify );
|
|
m_handleVerify = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Open the file (for R/W)
|
|
//
|
|
DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES ;
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH,
|
|
DesiredAccess,
|
|
EXCLUSIVE_FLAG,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&m_handleVerify ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( m_handleVerify );
|
|
|
|
//
|
|
// Tell the USN journal that we are the source of the changes.
|
|
//
|
|
WsbAffirmHr(m_pResource->GetPath(&volName, 0));
|
|
WsbAffirmHr(WsbMarkUsnSource(m_handleVerify, volName));
|
|
|
|
//
|
|
// Get the current attributes of the file and the times
|
|
//
|
|
WsbAffirmNtStatus( NtQueryInformationFile( m_handleVerify,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
fileVersionId = basicInformation.LastWriteTime.QuadPart;
|
|
|
|
//
|
|
// Set the time flags so that when we close the handle the
|
|
// times are not updated on the file and the FileAttributes
|
|
// indicate the file is offline
|
|
//
|
|
basicInformation.CreationTime.QuadPart = -1;
|
|
basicInformation.LastAccessTime.QuadPart = -1;
|
|
basicInformation.LastWriteTime.QuadPart = -1;
|
|
basicInformation.ChangeTime.QuadPart = -1;
|
|
basicInformation.FileAttributes = 0; // Do not change attributes yet
|
|
WsbAffirmNtStatus( NtSetInformationFile( m_handleVerify,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
//
|
|
// Do the check to see if the file changed
|
|
//
|
|
hr = VerifyInternal(offset, size, fileUsn1, fileUsn2);
|
|
|
|
//
|
|
// Note: Must check for S_OK since VerifyInternal may return FSA_E_ITEMCHANGED or FSA_E_ITEMINUSE
|
|
// Both are "Success hr", but should cause no truncation !!
|
|
//
|
|
if (S_OK != hr) {
|
|
WsbThrow(hr);
|
|
}
|
|
|
|
//
|
|
// Change the in memory flags for this scan item
|
|
//
|
|
m_findData.dwFileAttributes |= BIT_FOR_TRUNCATED;
|
|
|
|
//
|
|
// Rewrite the reparse point with the new flag
|
|
//
|
|
ntStatus = NtFsControlFile( m_handleVerify,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
FSCTL_SET_REPARSE_POINT,
|
|
pReparseBuffer,
|
|
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
|
|
+ pReparseBuffer->ReparseDataLength,
|
|
NULL,
|
|
0 );
|
|
|
|
//
|
|
// Check the return code
|
|
//
|
|
WsbAffirmNtStatus( ntStatus );
|
|
|
|
//
|
|
// It really happened so we need to flip the in memory copy of the
|
|
// isTruncated flag so it reflects reality
|
|
//
|
|
m_placeholder.isTruncated = TRUE;
|
|
|
|
//
|
|
// Set the file size to zero to truncate the file
|
|
sizeInformation.EndOfFile.QuadPart = 0 ;
|
|
WsbAffirmNtStatus( NtSetInformationFile( m_handleVerify,
|
|
&IoStatusBlock,
|
|
&sizeInformation,
|
|
sizeof( sizeInformation ),
|
|
FileEndOfFileInformation ) );
|
|
|
|
//
|
|
// Set the logical file size to the original size
|
|
sizeInformation.EndOfFile.QuadPart = m_placeholder.dataStreamSize;
|
|
WsbAffirmNtStatus( NtSetInformationFile( m_handleVerify,
|
|
&IoStatusBlock,
|
|
&sizeInformation,
|
|
sizeof( sizeInformation ),
|
|
FileEndOfFileInformation ) );
|
|
|
|
//
|
|
// Now that the truncation is complete we set the OFFLINE attribute.
|
|
//
|
|
basicInformation.CreationTime.QuadPart = -1; // Make sure we do nothing with dates
|
|
basicInformation.LastAccessTime.QuadPart = -1;
|
|
basicInformation.LastWriteTime.QuadPart = -1;
|
|
basicInformation.ChangeTime.QuadPart = -1;
|
|
basicInformation.FileAttributes = m_findData.dwFileAttributes;
|
|
WsbAffirmNtStatus(NtSetInformationFile( m_handleVerify,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ));
|
|
|
|
// Since we have restored the original attributes we can reset the flag that was possibly set by MakeReadWrite
|
|
m_changedAttributes = FALSE;
|
|
|
|
|
|
hr = S_OK;
|
|
} WsbCatch(hr);
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != m_handleVerify) {
|
|
NtClose( m_handleVerify );
|
|
m_handleVerify = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
// If the file data had changed (so we didn't truncate it) log event and
|
|
// remove placeholder info
|
|
if (FSA_E_ITEMCHANGED == hr) {
|
|
WsbLogEvent(FSA_MESSAGE_TRUNCSKIPPED_ISCHANGED, 0, NULL,
|
|
(OLECHAR*) jobName, WsbAbbreviatePath(fileName, 80),
|
|
WsbHrAsString(hr), NULL);
|
|
|
|
DeletePlaceholder(offset, size);
|
|
}
|
|
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::TruncateInternal"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::Verify(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::Verify().
|
|
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CWsbStringPtr path;
|
|
ULONG DesiredAccess;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::Verify"), OLESTR(""));
|
|
|
|
m_handleVerify = INVALID_HANDLE_VALUE;
|
|
|
|
try {
|
|
WsbAssert( m_gotPlaceholder, E_UNEXPECTED );
|
|
|
|
//
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES ;
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH,
|
|
DesiredAccess,
|
|
EXCLUSIVE_FLAG,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&m_handleVerify ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( m_handleVerify );
|
|
|
|
//
|
|
// Do the check to see if the file changed
|
|
// Note that it throws rather than affirms because FSA_E_ITEMCHANGED is a success
|
|
WsbThrow(VerifyInternal(offset, size, 0, 0));
|
|
|
|
} WsbCatch(hr);
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != m_handleVerify) {
|
|
NtClose( m_handleVerify );
|
|
m_handleVerify = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::Verify"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::VerifyInternal(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size,
|
|
IN LONGLONG compareUsn1,
|
|
IN LONGLONG compareUsn2
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::VerifyInternal().
|
|
|
|
|
|
Note: This requires that m_handleVerify is set up with a handle to the file being verified.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_BASIC_INFORMATION basicInformation;
|
|
CWsbStringPtr fileName;
|
|
CWsbStringPtr jobName;
|
|
LONGLONG fileVersionId;
|
|
ULONG i = 0;
|
|
CWsbStringPtr volName;
|
|
LONGLONG realFileSize;
|
|
RP_MSG in, out;
|
|
DWORD outSize;
|
|
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::VerifyInternal"), OLESTR(""));
|
|
|
|
// Get strings for tracing and error logging (ignore errors?!)
|
|
GetFullPathAndName( 0, 0, &fileName, 0);
|
|
m_pSession->GetName(&jobName, 0);
|
|
|
|
try {
|
|
BOOL DoCRC = FALSE;
|
|
BOOL DoUsnCheck = FALSE;
|
|
LONGLONG premigListUsn;
|
|
LONGLONG fileUsn;
|
|
|
|
|
|
|
|
WsbAffirmHr(GetLogicalSize(&realFileSize));
|
|
//
|
|
// Currently, avoid offset and size verification:
|
|
// - Since we are not doing partial file migration, offset is always 0.
|
|
// - Size in Validate would always be the same since it is taken from GetLogicalSize as well.
|
|
// - Size in case of auto-truncation is not reliable since it is taken from the premigrated db,
|
|
// where there could be bogus records from previous migrations of the file
|
|
//
|
|
/*** if ( (realFileSize != size) || (offset != 0) ) {
|
|
WsbThrow(FSA_E_ITEMCHANGED);
|
|
} ***/
|
|
UNREFERENCED_PARAMETER(size);
|
|
|
|
//
|
|
WsbAssertHandle( m_handleVerify );
|
|
|
|
//
|
|
// Get the USN from the premigration list and the USN from the file.
|
|
// We need to get the USN from the file now, before any NtSetInformationFile
|
|
// is called because this changes the USN value.
|
|
// If we have trouble getting the USN, just set them
|
|
// to 0 and go on, we check for 0 as a special case.
|
|
//
|
|
if (S_OK != GetPremigratedUsn(&premigListUsn)) {
|
|
premigListUsn = 0;
|
|
}
|
|
if (S_OK != WsbGetUsnFromFileHandle(m_handleVerify, FALSE, &fileUsn)) {
|
|
fileUsn = 0;
|
|
}
|
|
|
|
WsbTrace(OLESTR("CFsaScanItem::VerifyInternal: premig USN <%I64d>, file USN <%I64d>\n"),
|
|
premigListUsn, fileUsn );
|
|
WsbTrace(OLESTR("CFsaScanItem::VerifyInternal: Compare1 USN <%I64d>, Compare2 USN <%I64d>\n"),
|
|
compareUsn1, compareUsn2 );
|
|
//
|
|
// Get the current attributes of the file and the times
|
|
//
|
|
WsbAssertNtStatus( NtQueryInformationFile( m_handleVerify,
|
|
&IoStatusBlock,
|
|
(PVOID)&basicInformation,
|
|
sizeof( basicInformation ),
|
|
FileBasicInformation ) );
|
|
|
|
fileVersionId = basicInformation.LastWriteTime.QuadPart;
|
|
|
|
//
|
|
// Verify that the modify date & time has not changed since we
|
|
// took the data
|
|
//
|
|
if( fileVersionId != m_placeholder.fileVersionId ) {
|
|
WsbThrow(FSA_E_ITEMCHANGED);
|
|
}
|
|
|
|
//
|
|
// If the file is memory mapped by another process and the original handle was closed we
|
|
// are still able to open it for exclusive access here. We have to determine if the file
|
|
// is mapped and if so we cannot truncate it. The only way to do this is from kernel
|
|
// mode so we call our filter to do the check.
|
|
//
|
|
in.inout.command = RP_CHECK_HANDLE;
|
|
WsbAssertStatus(DeviceIoControl(m_handleVerify, FSCTL_HSM_MSG, &in,
|
|
sizeof(RP_MSG), &out, sizeof(RP_MSG), &outSize, NULL));
|
|
|
|
if (!out.msg.hRep.canTruncate) {
|
|
hr = FSA_E_ITEMINUSE;
|
|
WsbLogEvent(FSA_MESSAGE_TRUNCSKIPPED_ISMAPPED, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL);
|
|
WsbThrow(hr);
|
|
}
|
|
|
|
|
|
// If the USN's don't match, then we need to check the
|
|
// journal for changes
|
|
|
|
// premigListUsn: The usn of the file immediately after it was migrated
|
|
// compareUsn1: If not 0, the usn of the file before we (possibly) removed a read-only attribute
|
|
// compareUsn2: If not 0,
|
|
//
|
|
|
|
if ((0 == fileUsn) || (0 == premigListUsn)) {
|
|
// We don't have USN Journal info so force a CRC comparison
|
|
DoCRC = TRUE;
|
|
} else if ((compareUsn1 != 0) && (compareUsn2 != 0)) {
|
|
// Need to compare with these input usn instead of a direct compare
|
|
if ((premigListUsn != compareUsn1) || (fileUsn != compareUsn2)) {
|
|
DoUsnCheck = TRUE;
|
|
}
|
|
} else if (fileUsn != premigListUsn) {
|
|
DoUsnCheck = TRUE;
|
|
}
|
|
|
|
// Current usn indicates that file may have changed
|
|
if (DoUsnCheck) {
|
|
BOOL UsnChanged = FALSE;
|
|
|
|
hr = CheckUsnJournalForChanges(premigListUsn, fileUsn, &UsnChanged);
|
|
if (S_OK == hr) {
|
|
if (UsnChanged) {
|
|
// File changed, skip it
|
|
WsbThrow(FSA_E_ITEMCHANGED);
|
|
}
|
|
} else {
|
|
// Something failed, force a CRC comparison
|
|
DoCRC = TRUE;
|
|
WsbLogEvent(FSA_MESSAGE_USN_CHECK_FAILED, 0, NULL,
|
|
WsbAbbreviatePath(fileName,120),
|
|
WsbHrAsString(hr), NULL);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
// If the USNJ indicated a possible change, then we need to CRC
|
|
// the data.
|
|
if (DoCRC) {
|
|
//
|
|
// Check to be sure that the CRC in the placeholder matches
|
|
// that of the file
|
|
//
|
|
ULONG currentCRC;
|
|
|
|
#if defined(TEMPORARY_TRUNCATE_STATISTICS)
|
|
// Try to increment the truncate-CRC count in the registry
|
|
WsbIncRegistryValueDWORD(NULL, FSA_REGISTRY_PARMS,
|
|
OLESTR("TruncateCRCs"));
|
|
#endif
|
|
|
|
WsbAffirmHr(CalculateCurrentCRCInternal(m_handleVerify, offset, realFileSize, ¤tCRC));
|
|
WsbTrace(OLESTR("CFsaScanItem::VerifyInternal: Current CRC <%ul>, Reparse CRC <%ls>\n"),
|
|
currentCRC, WsbLonglongAsString( m_placeholder.dataStreamCRC ) );
|
|
if (currentCRC != m_placeholder.dataStreamCRC) {
|
|
//
|
|
// The file has changed since we migrated it so
|
|
// don't truncate it.
|
|
WsbThrow(FSA_E_ITEMCHANGED);
|
|
}
|
|
}
|
|
|
|
|
|
hr = S_OK;
|
|
} WsbCatch(hr);
|
|
|
|
|
|
// If the file data had changed (so we didn't truncate it) log event
|
|
// (cannot remove placeholder with DeletePlaceholder since the file is already opened exclusively
|
|
if (FSA_E_ITEMCHANGED == hr) {
|
|
WsbLogEvent(FSA_MESSAGE_TRUNCSKIPPED_ISCHANGED, 0, NULL,
|
|
(OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120),
|
|
WsbHrAsString(hr), NULL);
|
|
}
|
|
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::VerifyInternal"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT
|
|
CFsaScanItem::CheckIfSparse(
|
|
IN LONGLONG offset,
|
|
IN LONGLONG size
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
IFsaScanItem::CheckIfSparse()
|
|
|
|
Determines if the specified section is on disk or not (is sparse)
|
|
FSA_E_FILE_IS_TOTALLY_SPARSE - There are no resident portions of the section
|
|
FSA_E_FILE_IS_PARTIALLY_SPARSE - The section of the file has some resident/some sparse
|
|
sections
|
|
FSA_E_FILE_IS_NOT_SPARSE - The section is totally resident
|
|
any others - error and we don't know the state of the file
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
CWsbStringPtr path;
|
|
ULONG DesiredAccess;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS ntStatus;
|
|
FILE_ALLOCATED_RANGE_BUFFER inRange;
|
|
#define NUM_RANGE 10
|
|
FILE_ALLOCATED_RANGE_BUFFER outRange[NUM_RANGE];
|
|
PFILE_ALLOCATED_RANGE_BUFFER cRange;
|
|
int idx;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::CheckIfSparse"), OLESTR("offset = <%I64d>, size = <%I64d>"),
|
|
offset, size);
|
|
//
|
|
// If the file is really managed then we can check the allocation map
|
|
// Otherwise we indicate that the data is all resident
|
|
//
|
|
try {
|
|
//
|
|
// Create the real file name we need to open, under the covers this
|
|
// allocates the buffer since the path pointer is null
|
|
//
|
|
WsbAffirmHr( GetFullPathAndName( OLESTR("\\\\?\\"), NULL, &path, 0));
|
|
//WsbAffirmHr( GetFullPathAndName( NULL, NULL, &path, 0));
|
|
|
|
//
|
|
// Open the file to check the allocation
|
|
//
|
|
DesiredAccess = FILE_READ_ATTRIBUTES | FILE_READ_DATA;
|
|
WsbAffirmHr( OpenObject( path,
|
|
FILE_NON_DIRECTORY_FILE | FILE_NO_INTERMEDIATE_BUFFERING,
|
|
DesiredAccess,
|
|
SHARE_FLAGS,
|
|
FILE_OPEN,
|
|
&IoStatusBlock,
|
|
&handle ) );
|
|
|
|
//
|
|
// The open worked, our handle should be valid but we check to be
|
|
// safe and sure
|
|
//
|
|
WsbAssertHandle( handle );
|
|
|
|
memset(&outRange, 0, sizeof(FILE_ALLOCATED_RANGE_BUFFER) * NUM_RANGE);
|
|
|
|
//
|
|
// Check the allocation of the specified range
|
|
//
|
|
inRange.FileOffset.QuadPart = offset;
|
|
inRange.Length.QuadPart = size;
|
|
ntStatus = NtFsControlFile( handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
FSCTL_QUERY_ALLOCATED_RANGES,
|
|
&inRange,
|
|
sizeof(FILE_ALLOCATED_RANGE_BUFFER),
|
|
&outRange,
|
|
sizeof(FILE_ALLOCATED_RANGE_BUFFER) * NUM_RANGE);
|
|
|
|
//
|
|
// Check the return code but STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW are valid
|
|
//
|
|
if ( STATUS_SUCCESS != ntStatus && STATUS_BUFFER_OVERFLOW != ntStatus ) {
|
|
WsbAssertNtStatus( ntStatus );
|
|
}
|
|
|
|
|
|
cRange = (PFILE_ALLOCATED_RANGE_BUFFER) &outRange;
|
|
for (idx = 0; idx < NUM_RANGE; idx++) {
|
|
if (cRange->Length.QuadPart != 0) {
|
|
WsbTrace(OLESTR("CFsaScanItem::CheckIfSparse - Resident range %u Offset: %I64u, length: %I64u\n"),
|
|
idx, cRange->FileOffset.QuadPart, cRange->Length.QuadPart);
|
|
}
|
|
cRange++;
|
|
}
|
|
|
|
//
|
|
// Close the file since we are done with it and set the handle to invalid
|
|
//
|
|
NtClose(handle);
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// If the initial allocated range does begin where we said to start and the length of the
|
|
// allocated area is equal to the length we asked about then none of the data is sparse
|
|
//
|
|
if ( (outRange[0].FileOffset.QuadPart == offset) && (outRange[0].Length.QuadPart == size) ) {
|
|
hr = FSA_E_FILE_IS_NOT_SPARSE;
|
|
} else if (outRange[0].Length.QuadPart == 0) {
|
|
hr = FSA_E_FILE_IS_TOTALLY_SPARSE;
|
|
} else {
|
|
hr = FSA_E_FILE_IS_PARTIALLY_SPARSE;
|
|
}
|
|
|
|
} WsbCatch(hr);
|
|
|
|
//
|
|
// if we opened the file we need to close it
|
|
//
|
|
if( INVALID_HANDLE_VALUE != handle) {
|
|
NtClose( handle );
|
|
}
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::CheckIfSparse"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT
|
|
CFsaScanItem::CheckIfDiskFull(
|
|
void
|
|
)
|
|
|
|
/*++
|
|
|
|
Implements:
|
|
|
|
CFsaScanItem::CheckIfDiskFull()
|
|
|
|
Determines whether a disk is full or not - will consider full if free-space is below X,
|
|
where X default is 10 MB
|
|
|
|
Returns:
|
|
S_OK Disk is full
|
|
S_FALSE Disk is not full
|
|
other Unexpected error
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
WsbTraceIn(OLESTR("CFsaScanItem::CheckIfDiskFull"), OLESTR(""));
|
|
|
|
try {
|
|
ULARGE_INTEGER FreeBytesAvailableToCaller;
|
|
ULARGE_INTEGER TotalNumberOfBytes;
|
|
ULARGE_INTEGER TotalNumberOfFreeBytes;
|
|
|
|
// Get volume free space
|
|
CWsbStringPtr volumePath;
|
|
WsbAffirmHr(m_pResource->GetPath(&volumePath, 0));
|
|
|
|
WsbAffirmStatus(GetDiskFreeSpaceEx(volumePath,
|
|
&FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes));
|
|
|
|
// Get default from the Registry and compare
|
|
ULONG maxSizeDiskFullKB = FSA_MAX_SIZE_DISK_FULL_DEFAULT;
|
|
WsbAffirmHr(WsbRegistryValueUlongAsString(NULL, FSA_REGISTRY_PARMS, FSA_MAX_SIZE_DISK_FULL,
|
|
&maxSizeDiskFullKB));
|
|
|
|
if (maxSizeDiskFullKB == 0) {
|
|
// This means turning off quota check, i.e. always condiser disk as full
|
|
hr = S_OK;
|
|
} else if (TotalNumberOfFreeBytes.QuadPart <= (ULONGLONG )(maxSizeDiskFullKB * 1024)) {
|
|
// Disk is considered full
|
|
hr = S_OK;
|
|
} else {
|
|
// Disk free space is above the threshold
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
} WsbCatch(hr);
|
|
|
|
WsbTraceOut(OLESTR("CFsaScanItem::CheckIfDiskFull"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
|
|
|