www.pudn.com > filesys.rar > fileinfo.c
/*************************************************************************
*
* File: fileinfo.c
*
* Module: Sample File System Driver (Kernel mode execution only)
*
* Description:
* Contains code to handle the "set/query file information" dispatch
* entry points.
*
* Author: R. Nagar
*
* (c) 1996-97 Rajeev Nagar, All Rights Reserved
*
*************************************************************************/
#include "sfsd.h"
// define the file specific bug-check id
#define SFSD_BUG_CHECK_ID SFSD_FILE_INFORMATION
/*************************************************************************
*
* Function: SFsdFileInfo()
*
* Description:
* The I/O Manager will invoke this routine to handle a set/query file
* information request
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
* to be deferred to a worker thread context)
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdFileInfo(
PDEVICE_OBJECT DeviceObject, // the logical volume device object
PIRP Irp) // I/O Request Packet
{
NTSTATUS RC = STATUS_SUCCESS;
PtrSFsdIrpContext PtrIrpContext = NULL;
BOOLEAN AreWeTopLevel = FALSE;
FsRtlEnterFileSystem();
ASSERT(DeviceObject);
ASSERT(Irp);
// set the top level context
AreWeTopLevel = SFsdIsIrpTopLevel(Irp);
try {
// get an IRP context structure and issue the request
PtrIrpContext = SFsdAllocateIrpContext(Irp, DeviceObject);
ASSERT(PtrIrpContext);
RC = SFsdCommonFileInfo(PtrIrpContext, Irp);
} except (SFsdExceptionFilter(PtrIrpContext, GetExceptionInformation())) {
RC = SFsdExceptionHandler(PtrIrpContext, Irp);
SFsdLogEvent(SFSD_ERROR_INTERNAL_ERROR, RC);
}
if (AreWeTopLevel) {
IoSetTopLevelIrp(NULL);
}
FsRtlExitFileSystem();
return(RC);
}
/*************************************************************************
*
* Function: SFsdCommonFileInfo()
*
* Description:
* The actual work is performed here. This routine may be invoked in one'
* of the two possible contexts:
* (a) in the context of a system worker thread
* (b) in the context of the original caller
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdCommonFileInfo(
PtrSFsdIrpContext PtrIrpContext,
PIRP PtrIrp)
{
NTSTATUS RC = STATUS_SUCCESS;
PIO_STACK_LOCATION PtrIoStackLocation = NULL;
PFILE_OBJECT PtrFileObject = NULL;
PtrSFsdFCB PtrFCB = NULL;
PtrSFsdCCB PtrCCB = NULL;
PtrSFsdVCB PtrVCB = NULL;
PtrSFsdNTRequiredFCB PtrReqdFCB = NULL;
BOOLEAN MainResourceAcquired = FALSE;
BOOLEAN VCBResourceAcquired = FALSE;
BOOLEAN PagingIoResourceAcquired = FALSE;
IO_STATUS_BLOCK LocalIoStatus;
void *PtrSystemBuffer = NULL;
long BufferLength = 0;
FILE_INFORMATION_CLASS FunctionalityRequested;
BOOLEAN CanWait = FALSE;
BOOLEAN PostRequest = FALSE;
try {
// First, get a pointer to the current I/O stack location.
PtrIoStackLocation = IoGetCurrentIrpStackLocation(PtrIrp);
ASSERT(PtrIoStackLocation);
PtrFileObject = PtrIoStackLocation->FileObject;
ASSERT(PtrFileObject);
// Get the FCB and CCB pointers.
PtrCCB = (PtrSFsdCCB)(PtrFileObject->FsContext2);
ASSERT(PtrCCB);
PtrFCB = PtrCCB->PtrFCB;
ASSERT(PtrFCB);
PtrReqdFCB = &(PtrFCB->NTRequiredFCB);
CanWait = ((PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE);
// If the caller has opened a logical volume and is attempting to
// query information for it as a file stream, return an error.
if (PtrFCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_VCB) {
// This is not allowed. Caller must use get/set volume information instead.
RC = STATUS_INVALID_PARAMETER;
try_return(RC);
}
ASSERT(PtrFCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_FCB);
// The NT I/O Manager always allocates and supplies a system
// buffer for query and set file information calls.
// Copying information to/from the user buffer and the system
// buffer is performed by the I/O Manager and the FSD need not worry about it.
PtrSystemBuffer = PtrIrp->AssociatedIrp.SystemBuffer;
if (PtrIoStackLocation->MajorFunction == IRP_MJ_QUERY_INFORMATION) {
// Now, obtain some parameters.
BufferLength = PtrIoStackLocation->Parameters.QueryFile.Length;
FunctionalityRequested = PtrIoStackLocation->Parameters.QueryFile.FileInformationClass;
// Acquire the MainResource shared (NOTE: for paging-IO on a
// page file, we should avoid acquiring any resources and simply
// trust the VMM to do the right thing, else we could possibly
// run into deadlocks).
if (!(PtrFCB->FCBFlags & SFSD_FCB_PAGE_FILE)) {
// Acquire the MainResource shared.
if (!ExAcquireResourceSharedLite(&(PtrReqdFCB->MainResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
MainResourceAcquired = TRUE;
}
// Do whatever the caller asked us to do
switch (FunctionalityRequested) {
case FileBasicInformation:
RC = SFsdGetBasicInformation(PtrFCB, (PFILE_BASIC_INFORMATION)PtrSystemBuffer, &BufferLength);
break;
case FileStandardInformation:
// RC = SFsdGetStandardInformation(PtrFCB, PtrCCB, ...);
break;
// Similarly, implement all of the other query information routines
// that your FSD can support.
#if(_WIN32_WINNT >= 0x0400)
case FileNetworkOpenInformation:
// RC = SFsdGetNetworkOpenInformation(...);
break;
#endif // _WIN32_WINNT >= 0x0400
case FileInternalInformation:
// RC = SFsdGetInternalInformation(...);
break;
case FileEaInformation:
// RC = SFsdGetEaInformation(...);
break;
case FileNameInformation:
// RC = SFsdGetFullNameInformation(...);
break;
case FileAlternateNameInformation:
// RC = SFsdGetAltNameInformation(...);
break;
case FileCompressionInformation:
// RC = SFsdGetCompressionInformation(...);
break;
case FilePositionInformation:
// This is fairly simple. Copy over the information from the
// file object.
{
PFILE_POSITION_INFORMATION PtrFileInfoBuffer;
PtrFileInfoBuffer = (PFILE_POSITION_INFORMATION)PtrSystemBuffer;
ASSERT(BufferLength >= sizeof(FILE_POSITION_INFORMATION));
PtrFileInfoBuffer->CurrentByteOffset = PtrFileObject->CurrentByteOffset;
// Modify the local variable for BufferLength appropriately.
BufferLength -= sizeof(FILE_POSITION_INFORMATION);
}
break;
case FileStreamInformation:
// RC = SFsdGetFileStreamInformation(...);
break;
case FileAllInformation:
// The I/O Manager supplies the Mode, Access, and Alignment
// information. The rest is up to us to provide.
// Therefore, decrement the BufferLength appropriately (assuming
// that the above 3 types on information are already in the
// buffer)
{
PFILE_POSITION_INFORMATION PtrFileInfoBuffer;
PFILE_ALL_INFORMATION PtrAllInfo = (PFILE_ALL_INFORMATION)PtrSystemBuffer;
BufferLength -= (sizeof(FILE_MODE_INFORMATION) + sizeof(FILE_ACCESS_INFORMATION) + sizeof(FILE_ALIGNMENT_INFORMATION));
// Fill in the position information.
PtrFileInfoBuffer = (PFILE_POSITION_INFORMATION)&(PtrAllInfo->PositionInformation);
PtrFileInfoBuffer->CurrentByteOffset = PtrFileObject->CurrentByteOffset;
// Modify the local variable for BufferLength appropriately.
ASSERT(BufferLength >= sizeof(FILE_POSITION_INFORMATION));
BufferLength -= sizeof(FILE_POSITION_INFORMATION);
// Get the remaining stuff.
if (!NT_SUCCESS(RC = SFsdGetBasicInformation(PtrFCB, (PFILE_BASIC_INFORMATION)&(PtrAllInfo->BasicInformation),
&BufferLength))) {
// Another method you may wish to use to avoid the
// multiple checks for success/failure is to have the
// called routine simply raise an exception instead.
try_return(RC);
}
// Similarly, get all of the others ...
}
break;
default:
RC = STATUS_INVALID_PARAMETER;
try_return(RC);
}
// If we completed successfully, the return the amount of information transferred.
if (NT_SUCCESS(RC)) {
PtrIrp->IoStatus.Information = PtrIoStackLocation->Parameters.QueryFile.Length - BufferLength;
} else {
PtrIrp->IoStatus.Information = 0;
}
} else {
ASSERT(PtrIoStackLocation->MajorFunction == IRP_MJ_SET_INFORMATION);
// Now, obtain some parameters.
FunctionalityRequested = PtrIoStackLocation->Parameters.SetFile.FileInformationClass;
// If your FSD supports opportunistic locking (described in
// Chapter 11), then you should check whether the oplock state
// allows the caller to proceed.
// Rename, and link operations require creation of a directory
// entry and possibly deletion of another directory entry. Since,
// we acquire the VCB resource exclusively during create operations,
// we should acquire it exclusively for link and/or rename operations
// as well.
// Similarly, marking a directory entry for deletion should cause us
// to acquire the VCB exclusively as well.
if ((FunctionalityRequested == FileDispositionInformation) || (FunctionalityRequested == FileRenameInformation) ||
(FunctionalityRequested == FileLinkInformation)) {
if (!ExAcquireResourceExclusiveLite(&(PtrVCB->VCBResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
// We have the VCB acquired exclusively.
VCBResourceAcquired = TRUE;
}
// Unless this is an operation on a page file, we should go ahead and
// acquire the FCB exclusively at this time. Note that we will pretty
// much block out anything being done to the FCB from this point on.
if (!(PtrFCB->FCBFlags & SFSD_FCB_PAGE_FILE)) {
// Acquire the MainResource shared.
if (!ExAcquireResourceExclusiveLite(&(PtrReqdFCB->MainResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
MainResourceAcquired = TRUE;
}
// The only operations that could conceivably proceed from this point
// on are paging-IO read/write operations. For delete link (rename),
// set allocation size, and set EOF, should also acquire the paging-IO
// resource, thereby synchronizing with paging-IO requests. In your
// FSD, you should ideally acquire the resource only when processing
// such requests; here however, I will go ahead and block out all
// paging-IO read/write operations at this time (for convenience).
if (!ExAcquireResourceExclusiveLite(&(PtrReqdFCB->PagingIoResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
// Do whatever the caller asked us to do
switch (FunctionalityRequested) {
case FileBasicInformation:
RC = SFsdSetBasicInformation(PtrFCB, PtrCCB, PtrFileObject, (PFILE_BASIC_INFORMATION)PtrSystemBuffer);
break;
case FilePositionInformation:
// Check if no intermediate buffering has been specified.
// If it was specified, do not allow non-aligned set file
// position requests to succeed.
{
PFILE_POSITION_INFORMATION PtrFileInfoBuffer;
PtrFileInfoBuffer = (PFILE_POSITION_INFORMATION)PtrSystemBuffer;
if (PtrFileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
if (PtrFileInfoBuffer->CurrentByteOffset.LowPart & PtrIoStackLocation->DeviceObject->AlignmentRequirement) {
// Invalid alignment.
try_return(RC = STATUS_INVALID_PARAMETER);
}
}
PtrFileObject->CurrentByteOffset = PtrFileInfoBuffer->CurrentByteOffset;
}
break;
case FileDispositionInformation:
RC = SFsdSetDispositionInformation(PtrFCB, PtrCCB, PtrVCB, PtrFileObject, PtrIrpContext, PtrIrp,
(PFILE_DISPOSITION_INFORMATION)PtrSystemBuffer);
break;
case FileRenameInformation:
case FileLinkInformation:
// When you implement your rename/link routine, be careful to
// check the following two arguments:
// TargetFileObject = PtrIoStackLocation->Parameters.SetFile.FileObject;
// ReplaceExistingFile = PtrIoStackLocation->Parameters.SetFile.ReplaceIfExists;
//
// The TargetFileObject argument is a pointer to the "target
// directory" file object obtained during the "create" routine
// invoked by the NT I/O Manager with the SL_OPEN_TARGET_DIRECTORY
// flag specified. Remember that it is quite possible that if the
// rename/link is contained within a single directory, the target
// and source directories will be the same.
// The ReplaceExistingFile argument should be used by you to
// determine if the caller wishes to replace the target (if it
// currently exists) with the new link/renamed file. If this
// value is FALSE, and if the target directory entry (being
// renamed-to, or the target of the link) exists, you should
// return a STATUS_OBJECT_NAME_COLLISION error to the caller.
// RC = SFsdRenameOrLinkFile(PtrFCB, PtrCCB, PtrFileObject, PtrIrpContext,
// PtrIrp, (PFILE_RENAME_INFORMATION)PtrSystemBuffer);
// Once you have completed the rename/link operation, do not
// forget to notify any "notify IRPs" about the actions you have
// performed.
// An example is if you renamed across directories, you should
// report that a new entry was added with the FILE_ACTION_ADDED
// action type. The actual modification would then be reported
// as either FILE_NOTIFY_CHANGE_FILE_NAME (if a file was renamed)
// or FILE_NOTIFY_CHANGE_DIR_NAME (if a directory was renamed).
break;
case FileAllocationInformation:
RC = SFsdSetAllocationInformation(PtrFCB, PtrCCB, PtrVCB, PtrFileObject,
PtrIrpContext, PtrIrp, PtrSystemBuffer);
break;
case FileEndOfFileInformation:
// RC = SFsdSetEOF(...);
break;
default:
RC = STATUS_INVALID_PARAMETER;
try_return(RC);
}
}
try_exit: NOTHING;
} finally {
if (PagingIoResourceAcquired) {
SFsdReleaseResource(&(PtrReqdFCB->PagingIoResource));
PagingIoResourceAcquired = FALSE;
}
if (MainResourceAcquired) {
SFsdReleaseResource(&(PtrReqdFCB->MainResource));
MainResourceAcquired = FALSE;
}
if (VCBResourceAcquired) {
SFsdReleaseResource(&(PtrVCB->VCBResource));
VCBResourceAcquired = FALSE;
}
// Post IRP if required
if (PostRequest) {
// Since, the I/O Manager gave us a system buffer, we do not
// need to "lock" anything.
// Perform the post operation which will mark the IRP pending
// and will return STATUS_PENDING back to us
RC = SFsdPostRequest(PtrIrpContext, PtrIrp);
} else {
// Can complete the IRP here if no exception was encountered
if (!(PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_EXCEPTION)) {
PtrIrp->IoStatus.Status = RC;
// Free up the Irp Context
SFsdReleaseIrpContext(PtrIrpContext);
// complete the IRP
IoCompleteRequest(PtrIrp, IO_DISK_INCREMENT);
}
} // can we complete the IRP ?
} // end of "finally" processing
return(RC);
}
/*************************************************************************
*
* Function: SFsdGetBasicInformation()
*
* Description:
* Return some time-stamps and file attributes to the caller.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdGetBasicInformation(
PtrSFsdFCB PtrFCB,
PFILE_BASIC_INFORMATION PtrBuffer,
long *PtrReturnedLength)
{
NTSTATUS RC = STATUS_SUCCESS;
try {
if (*PtrReturnedLength < sizeof(FILE_BASIC_INFORMATION)) {
try_return(RC = STATUS_BUFFER_OVERFLOW);
}
// Zero out the supplied buffer.
RtlZeroMemory(PtrBuffer, sizeof(FILE_BASIC_INFORMATION));
// Note: If your FSD wishes to be even more precise about time
// stamps, you may wish to consider the effects of fast-IO on the
// file stream. Typically, the FSD simply states a flag indicating
// that fast-IO read/write has occured. Time stamps are then updated
// when a cleanup is received for the file stream. However, if the
// user performs fast-IO and subsequently issues a request to query
// basic information, your FSD could query the current system time
// using KeQuerySystemTime(), and update the FCB time-stamps before
// returning the values to the caller. This gives the caller a slightly
// more accurate value.
// Get information from the FCB.
PtrBuffer->CreationTime = PtrFCB->CreationTime;
PtrBuffer->LastAccessTime = PtrFCB->LastAccessTime;
PtrBuffer->LastWriteTime = PtrFCB->LastWriteTime;
// Assume that the sample FSD does not support a "change time".
// Now fill in the attributes.
PtrBuffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
if (PtrFCB->FCBFlags & SFSD_FCB_DIRECTORY) {
PtrBuffer->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
}
// Similarly, fill in attributes indicating a hidden file, system
// file, compressed file, temporary file, etc. if your FSD supports
// such file attribute values.
try_exit: NOTHING;
} finally {
if (NT_SUCCESS(RC)) {
// Return the amount of information filled in.
*PtrReturnedLength -= sizeof(FILE_BASIC_INFORMATION);
}
}
return(RC);
}
/*************************************************************************
*
* Function: SFsdSetBasicInformation()
*
* Description:
* Set some time-stamps and file attributes supplied by the caller.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdSetBasicInformation(
PtrSFsdFCB PtrFCB,
PtrSFsdCCB PtrCCB,
PFILE_OBJECT PtrFileObject,
PFILE_BASIC_INFORMATION PtrBuffer)
{
NTSTATUS RC = STATUS_SUCCESS;
BOOLEAN CreationTimeChanged = FALSE;
BOOLEAN AttributesChanged = FALSE;
try {
// Obtain a pointer to the directory entry associated with
// the FCB being modifed. The directory entry is obviously
// part of the data associated with the parent directory that
// contains this particular file stream.
// Note well that no other modifications
// are currently allowed to the directory entry since we have
// the VCB resource exclusively acquired (as a matter of fact,
// NO directory on the logical volume can be currently modified).
// PtrDirectoryEntry = SFsdGetDirectoryEntryPtr(...);
if (RtlLargeIntegerNotEqualToZero(PtrBuffer->CreationTime)) {
// Modify the directory entry timestamp.
// ...
// Also note that fact that the timestamp has changed
// so that any directory notifications can be performed.
CreationTimeChanged = TRUE;
// The interesting thing here is that the user has set certain time
// fields. However, before doing this, the user may have performed
// I/O which in turn would have caused your FSD to mark the fact that
// write/access time should be modifed at cleanup.
// You might wish to mark the fact that such updates are no longer
// required since the user has explicitly specified the values she
// wishes to see associated with the file stream.
SFsdSetFlag(PtrCCB->CCBFlags, SFSD_CCB_CREATE_TIME_SET);
}
// Similarly, check for all the time-stamp values that your
// FSD cares about. Ignore the ones that you do not support.
// ...
// Now come the attributes.
if (PtrBuffer->FileAttributes) {
// We have a non-zero attribute value.
// The presence of a particular attribute indicates that the
// user wishes to set the attribute value. The absence indicates
// the user wishes to clear the particular attribute.
// Before we start examining attribute values, you may wish
// to clear any unsupported attribute flags to reduce confusion.
SFsdClearFlag(PtrBuffer->FileAttributes, ~FILE_ATTRIBUTE_VALID_SET_FLAGS);
SFsdClearFlag(PtrBuffer->FileAttributes, FILE_ATTRIBUTE_NORMAL);
// Similarly, you should pick out other invalid flag values.
// SFsdClearFlag(PtrBuffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ATOMIC_WRITE ...);
if (PtrBuffer->FileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
SFsdSetFlag(PtrFileObject->Flags, FO_TEMPORARY_FILE);
} else {
SFsdClearFlag(PtrFileObject->Flags, FO_TEMPORARY_FILE);
}
// If your FSD supports file compression, you may wish to
// note the user's preferences for compressing/not compressing
// the file at this time.
}
try_exit: NOTHING;
} finally {
;
}
return(RC);
}
/*************************************************************************
*
* Function: SFsdSetDispositionInformation()
*
* Description:
* Mark/Unmark a file for deletion.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdSetDispositionInformation(
PtrSFsdFCB PtrFCB,
PtrSFsdCCB PtrCCB,
PtrSFsdVCB PtrVCB,
PFILE_OBJECT PtrFileObject,
PtrSFsdIrpContext PtrIrpContext,
PIRP PtrIrp,
PFILE_DISPOSITION_INFORMATION PtrBuffer)
{
NTSTATUS RC = STATUS_SUCCESS;
try {
if (!PtrBuffer->DeleteFile) {
// "un-delete" the file.
SFsdClearFlag(PtrFCB->FCBFlags, SFSD_FCB_DELETE_ON_CLOSE);
PtrFileObject->DeletePending = FALSE;
try_return(RC);
}
// The easy part is over. Now, we know that the user wishes to
// delete the corresponding directory entry (of course, if this
// is the only link to the file stream, any on-disk storage space
// associated with the file stream will also be released when the
// (only) link is deleted!)
// Do some checking to see if the file can even be deleted.
if (PtrFCB->FCBFlags & SFSD_FCB_DELETE_ON_CLOSE) {
// All done!
try_return(RC);
}
if (PtrFCB->FCBFlags & SFSD_FCB_READ_ONLY) {
try_return(RC = STATUS_CANNOT_DELETE);
}
if (PtrVCB->VCBFlags & SFSD_VCB_FLAGS_VOLUME_READ_ONLY) {
try_return(RC = STATUS_CANNOT_DELETE);
}
// An important step is to check if the file stream has been
// mapped by any process. The delete cannot be allowed to proceed
// in this case.
if (!MmFlushImageSection(&(PtrFCB->NTRequiredFCB.SectionObject), MmFlushForDelete)) {
try_return(RC = STATUS_CANNOT_DELETE);
}
// It would not be prudent to allow deletion of either a root
// directory or a directory that is not empty.
if (PtrFCB->FCBFlags & SFSD_FCB_ROOT_DIRECTORY) {
try_return(RC = STATUS_CANNOT_DELETE);
}
if (PtrFCB->FCBFlags & SFSD_FCB_DIRECTORY) {
// Perform your check to determine whether the directory
// is empty or not.
// if (!SFsdIsDirectoryEmpty(PtrFCB, PtrCCB, PtrIrpContext)) {
// try_return(RC = STATUS_DIRECTORY_NOT_EMPTY);
// }
}
// Set a flag to indicate that this directory entry will become history
// at cleanup.
SFsdSetFlag(PtrFCB->FCBFlags, SFSD_FCB_DELETE_ON_CLOSE);
PtrFileObject->DeletePending = TRUE;
try_exit: NOTHING;
} finally {
;
}
return(RC);
}
/*************************************************************************
*
* Function: SFsdSetAllocationInformation()
*
* Description:
* Mark/Unmark a file for deletion.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdSetAllocationInformation(
PtrSFsdFCB PtrFCB,
PtrSFsdCCB PtrCCB,
PtrSFsdVCB PtrVCB,
PFILE_OBJECT PtrFileObject,
PtrSFsdIrpContext PtrIrpContext,
PIRP PtrIrp,
PFILE_ALLOCATION_INFORMATION PtrBuffer)
{
NTSTATUS RC = STATUS_SUCCESS;
BOOLEAN TruncatedFile = FALSE;
BOOLEAN ModifiedAllocSize = FALSE;
try {
// Increasing the allocation size associated with a file stream
// is relatively easy. All you have to do is execute some FSD
// specific code to check whether you have enough space available
// (and if your FSD supports user/volume quotas, whether the user
// is not exceeding quota), and then increase the file size in the
// corresponding on-disk and in-memory structures.
// Then, all you should do is inform the Cache Manager about the
// increased allocation size.
// First, do whatever error checking is appropriate here (e.g. whether
// the caller is trying the change size for a directory, etc.).
// Are we increasing the allocation size?
if (RtlLargeIntegerLessThan(
PtrFCB->NTRequiredFCB.CommonFCBHeader.AllocationSize,
PtrBuffer->AllocationSize)) {
// Yes. Do the FSD specific stuff i.e. increase reserved
// space on disk.
// RC = SFsdTruncateFileAllocationSize(...)
ModifiedAllocSize = TRUE;
} else if (RtlLargeIntegerGreaterThan(PtrFCB->NTRequiredFCB.CommonFCBHeader.AllocationSize,
PtrBuffer->AllocationSize)) {
// This is the painful part. See if the VMM will allow us to proceed.
// The VMM will deny the request if:
// (a) any image section exists OR
// (b) a data section exists and the size of the user mapped view
// is greater than the new size
// Otherwise, the VMM should allow the request to proceed.
if (!MmCanFileBeTruncated(&(PtrFCB->NTRequiredFCB.SectionObject), &(PtrBuffer->AllocationSize))) {
// VMM said no way!
try_return(RC = STATUS_USER_MAPPED_FILE);
}
// Perform your directory entry modifications. Release any on-disk
// space you may need to in the process.
// RC = SFsdTruncateFileAllocationSize(...);
ModifiedAllocSize = TRUE;
TruncatedFile = TRUE;
}
try_exit:
// This is a good place to check if we have performed a truncate
// operation. If we have perform a truncate (whether we extended
// or reduced file size), you should update file time stamps.
// Last, but not the lease, you must inform the Cache Manager of file size changes.
if (ModifiedAllocSize && NT_SUCCESS(RC)) {
// Update the FCB Header with the new allocation size.
PtrFCB->NTRequiredFCB.CommonFCBHeader.AllocationSize = PtrBuffer->AllocationSize;
// If we decreased the allocation size to less than the
// current file size, modify the file size value.
// Similarly, if we decreased the value to less than the
// current valid data length, modify that value as well.
if (TruncatedFile) {
if (RtlLargeIntegerLessThan(PtrFCB->NTRequiredFCB.CommonFCBHeader.FileSize, PtrBuffer->AllocationSize)) {
// Decrease the file size value.
PtrFCB->NTRequiredFCB.CommonFCBHeader.FileSize = PtrBuffer->AllocationSize;
}
if (RtlLargeIntegerLessThan(PtrFCB->NTRequiredFCB.CommonFCBHeader.ValidDataLength, PtrBuffer->AllocationSize)) {
// Decrease the valid data length value.
PtrFCB->NTRequiredFCB.CommonFCBHeader.ValidDataLength = PtrBuffer->AllocationSize;
}
}
// If the FCB has not had caching initiated, it is still valid
// for you to invoke the NT Cache Manager. It is possible in such
// situations for the call to be no'oped (unless some user has
// mapped in the file)
// NOTE: The invocation to CcSetFileSizes() will quite possibly
// result in a recursive call back into the file system.
// This is because the NT Cache Manager will typically
// perform a flush before telling the VMM to purge pages
// especially when caching has not been initiated on the
// file stream, but the user has mapped the file into
// the process' virtual address space.
CcSetFileSizes(PtrFileObject, (PCC_FILE_SIZES)&(PtrFCB->NTRequiredFCB.CommonFCBHeader.AllocationSize));
// Inform any pending IRPs (notify change directory).
}
} finally {
;
}
return(RC);
}