reactos/drivers/filesystems/fastfat/create.c
2018-08-21 08:36:51 +02:00

1111 lines
37 KiB
C

/*
* ReactOS kernel
* Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* PROJECT: ReactOS kernel
* FILE: drivers/filesystems/fastfat/create.c
* PURPOSE: VFAT Filesystem
* PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
* Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include "vfat.h"
#define NDEBUG
#include <debug.h>
/* FUNCTIONS *****************************************************************/
VOID
vfat8Dot3ToString(
PFAT_DIR_ENTRY pEntry,
PUNICODE_STRING NameU)
{
OEM_STRING StringA;
USHORT Length;
CHAR cString[12];
RtlCopyMemory(cString, pEntry->ShortName, 11);
cString[11] = 0;
if (cString[0] == 0x05)
{
cString[0] = 0xe5;
}
StringA.Buffer = cString;
for (StringA.Length = 0;
StringA.Length < 8 && StringA.Buffer[StringA.Length] != ' ';
StringA.Length++);
StringA.MaximumLength = StringA.Length;
RtlOemStringToUnicodeString(NameU, &StringA, FALSE);
if (BooleanFlagOn(pEntry->lCase, VFAT_CASE_LOWER_BASE))
{
RtlDowncaseUnicodeString(NameU, NameU, FALSE);
}
if (cString[8] != ' ')
{
Length = NameU->Length;
NameU->Buffer += Length / sizeof(WCHAR);
if (!FAT_ENTRY_VOLUME(pEntry))
{
Length += sizeof(WCHAR);
NameU->Buffer[0] = L'.';
NameU->Buffer++;
}
NameU->Length = 0;
NameU->MaximumLength -= Length;
StringA.Buffer = &cString[8];
for (StringA.Length = 0;
StringA.Length < 3 && StringA.Buffer[StringA.Length] != ' ';
StringA.Length++);
StringA.MaximumLength = StringA.Length;
RtlOemStringToUnicodeString(NameU, &StringA, FALSE);
if (BooleanFlagOn(pEntry->lCase, VFAT_CASE_LOWER_EXT))
{
RtlDowncaseUnicodeString(NameU, NameU, FALSE);
}
NameU->Buffer -= Length / sizeof(WCHAR);
NameU->Length += Length;
NameU->MaximumLength += Length;
}
NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0;
DPRINT("'%wZ'\n", NameU);
}
/*
* FUNCTION: Find a file
*/
NTSTATUS
FindFile(
PDEVICE_EXTENSION DeviceExt,
PVFATFCB Parent,
PUNICODE_STRING FileToFindU,
PVFAT_DIRENTRY_CONTEXT DirContext,
BOOLEAN First)
{
PWCHAR PathNameBuffer;
USHORT PathNameBufferLength;
NTSTATUS Status;
PVOID Context = NULL;
PVOID Page;
PVFATFCB rcFcb;
BOOLEAN Found;
UNICODE_STRING PathNameU;
UNICODE_STRING FileToFindUpcase;
BOOLEAN WildCard;
BOOLEAN IsFatX = vfatVolumeIsFatX(DeviceExt);
DPRINT("FindFile(Parent %p, FileToFind '%wZ', DirIndex: %u)\n",
Parent, FileToFindU, DirContext->DirIndex);
DPRINT("FindFile: Path %wZ\n",&Parent->PathNameU);
PathNameBufferLength = LONGNAME_MAX_LENGTH * sizeof(WCHAR);
PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength + sizeof(WCHAR), TAG_NAME);
if (!PathNameBuffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
PathNameU.Buffer = PathNameBuffer;
PathNameU.Length = 0;
PathNameU.MaximumLength = PathNameBufferLength;
DirContext->LongNameU.Length = 0;
DirContext->ShortNameU.Length = 0;
WildCard = FsRtlDoesNameContainWildCards(FileToFindU);
if (WildCard == FALSE)
{
/* if there is no '*?' in the search name, than look first for an existing fcb */
RtlCopyUnicodeString(&PathNameU, &Parent->PathNameU);
if (!vfatFCBIsRoot(Parent))
{
PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = L'\\';
PathNameU.Length += sizeof(WCHAR);
}
RtlAppendUnicodeStringToString(&PathNameU, FileToFindU);
PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0;
rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU);
if (rcFcb)
{
ULONG startIndex = rcFcb->startIndex;
if (IsFatX && !vfatFCBIsRoot(Parent))
{
startIndex += 2;
}
if(startIndex >= DirContext->DirIndex)
{
RtlCopyUnicodeString(&DirContext->LongNameU, &rcFcb->LongNameU);
RtlCopyUnicodeString(&DirContext->ShortNameU, &rcFcb->ShortNameU);
RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY));
DirContext->StartIndex = rcFcb->startIndex;
DirContext->DirIndex = rcFcb->dirIndex;
DPRINT("FindFile: new Name %wZ, DirIndex %u (%u)\n",
&DirContext->LongNameU, DirContext->DirIndex, DirContext->StartIndex);
Status = STATUS_SUCCESS;
}
else
{
DPRINT("FCB not found for %wZ\n", &PathNameU);
Status = STATUS_UNSUCCESSFUL;
}
vfatReleaseFCB(DeviceExt, rcFcb);
ExFreePoolWithTag(PathNameBuffer, TAG_NAME);
return Status;
}
}
/* FsRtlIsNameInExpression need the searched string to be upcase,
* even if IgnoreCase is specified */
Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFindU, TRUE);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(PathNameBuffer, TAG_NAME);
return Status;
}
while (TRUE)
{
Status = VfatGetNextDirEntry(DeviceExt, &Context, &Page, Parent, DirContext, First);
First = FALSE;
if (Status == STATUS_NO_MORE_ENTRIES)
{
break;
}
if (ENTRY_VOLUME(IsFatX, &DirContext->DirEntry))
{
DirContext->DirIndex++;
continue;
}
if (DirContext->LongNameU.Length == 0 ||
DirContext->ShortNameU.Length == 0)
{
DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
{
ASSERT(DirContext->LongNameU.Length != 0 &&
DirContext->ShortNameU.Length != 0);
}
DirContext->DirIndex++;
continue;
}
if (WildCard)
{
Found = FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->LongNameU, TRUE, NULL) ||
FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->ShortNameU, TRUE, NULL);
}
else
{
Found = FsRtlAreNamesEqual(&DirContext->LongNameU, FileToFindU, TRUE, NULL) ||
FsRtlAreNamesEqual(&DirContext->ShortNameU, FileToFindU, TRUE, NULL);
}
if (Found)
{
if (WildCard)
{
RtlCopyUnicodeString(&PathNameU, &Parent->PathNameU);
if (!vfatFCBIsRoot(Parent))
{
PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = L'\\';
PathNameU.Length += sizeof(WCHAR);
}
RtlAppendUnicodeStringToString(&PathNameU, &DirContext->LongNameU);
PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0;
rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU);
if (rcFcb != NULL)
{
RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY));
vfatReleaseFCB(DeviceExt, rcFcb);
}
}
DPRINT("%u\n", DirContext->LongNameU.Length);
DPRINT("FindFile: new Name %wZ, DirIndex %u\n",
&DirContext->LongNameU, DirContext->DirIndex);
if (Context)
{
CcUnpinData(Context);
}
RtlFreeUnicodeString(&FileToFindUpcase);
ExFreePoolWithTag(PathNameBuffer, TAG_NAME);
return STATUS_SUCCESS;
}
DirContext->DirIndex++;
}
if (Context)
{
CcUnpinData(Context);
}
RtlFreeUnicodeString(&FileToFindUpcase);
ExFreePoolWithTag(PathNameBuffer, TAG_NAME);
return Status;
}
/*
* FUNCTION: Opens a file
*/
static
NTSTATUS
VfatOpenFile(
PDEVICE_EXTENSION DeviceExt,
PUNICODE_STRING PathNameU,
PFILE_OBJECT FileObject,
ULONG RequestedDisposition,
ULONG RequestedOptions,
PVFATFCB *ParentFcb)
{
PVFATFCB Fcb;
NTSTATUS Status;
DPRINT("VfatOpenFile(%p, '%wZ', %p, %p)\n", DeviceExt, PathNameU, FileObject, ParentFcb);
if (FileObject->RelatedFileObject)
{
DPRINT("'%wZ'\n", &FileObject->RelatedFileObject->FileName);
*ParentFcb = FileObject->RelatedFileObject->FsContext;
}
else
{
*ParentFcb = NULL;
}
if (!DeviceExt->FatInfo.FixedMedia)
{
Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice,
IOCTL_DISK_CHECK_VERIFY,
NULL,
0,
NULL,
0,
FALSE);
if (!NT_SUCCESS(Status))
{
DPRINT("Status %lx\n", Status);
*ParentFcb = NULL;
return Status;
}
}
if (*ParentFcb)
{
vfatGrabFCB(DeviceExt, *ParentFcb);
}
/* try first to find an existing FCB in memory */
DPRINT("Checking for existing FCB in memory\n");
Status = vfatGetFCBForFile(DeviceExt, ParentFcb, &Fcb, PathNameU);
if (!NT_SUCCESS(Status))
{
DPRINT ("Could not make a new FCB, status: %x\n", Status);
return Status;
}
/* Fail, if we try to overwrite an existing directory */
if ((!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) && vfatFCBIsDirectory(Fcb)) &&
(RequestedDisposition == FILE_OVERWRITE ||
RequestedDisposition == FILE_OVERWRITE_IF ||
RequestedDisposition == FILE_SUPERSEDE))
{
vfatReleaseFCB(DeviceExt, Fcb);
return STATUS_OBJECT_NAME_COLLISION;
}
if (BooleanFlagOn(Fcb->Flags, FCB_DELETE_PENDING))
{
vfatReleaseFCB(DeviceExt, Fcb);
return STATUS_DELETE_PENDING;
}
/* Fail, if we try to overwrite a read-only file */
if (vfatFCBIsReadOnly(Fcb) &&
(RequestedDisposition == FILE_OVERWRITE ||
RequestedDisposition == FILE_OVERWRITE_IF))
{
vfatReleaseFCB(DeviceExt, Fcb);
return STATUS_ACCESS_DENIED;
}
if (vfatFCBIsReadOnly(Fcb) &&
(RequestedOptions & FILE_DELETE_ON_CLOSE))
{
vfatReleaseFCB(DeviceExt, Fcb);
return STATUS_CANNOT_DELETE;
}
if ((vfatFCBIsRoot(Fcb) ||
(Fcb->LongNameU.Length == sizeof(WCHAR) && Fcb->LongNameU.Buffer[0] == L'.') ||
(Fcb->LongNameU.Length == 2 * sizeof(WCHAR) && Fcb->LongNameU.Buffer[0] == L'.' && Fcb->LongNameU.Buffer[1] == L'.')) &&
BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
{
// we cannot delete a '.', '..' or the root directory
vfatReleaseFCB(DeviceExt, Fcb);
return STATUS_CANNOT_DELETE;
}
/* If that one was marked for closing, remove it */
if (BooleanFlagOn(Fcb->Flags, FCB_DELAYED_CLOSE))
{
BOOLEAN ConcurrentDeletion;
PVFAT_CLOSE_CONTEXT CloseContext;
/* Get the context */
CloseContext = Fcb->CloseContext;
/* Is someone already taking over? */
if (CloseContext != NULL)
{
ConcurrentDeletion = FALSE;
/* Lock list */
ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
/* Check whether it was already removed, if not, do it */
if (!IsListEmpty(&CloseContext->CloseListEntry))
{
RemoveEntryList(&CloseContext->CloseListEntry);
--VfatGlobalData->CloseCount;
ConcurrentDeletion = TRUE;
}
ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
/* It's not delayed anymore! */
ClearFlag(Fcb->Flags, FCB_DELAYED_CLOSE);
/* Release the extra reference (would have been removed by IRP_MJ_CLOSE) */
vfatReleaseFCB(DeviceExt, Fcb);
Fcb->CloseContext = NULL;
/* If no concurrent deletion, free work item */
if (!ConcurrentDeletion)
{
ExFreeToPagedLookasideList(&VfatGlobalData->CloseContextLookasideList, CloseContext);
}
}
DPRINT("Reusing delayed close FCB for %wZ\n", &Fcb->PathNameU);
}
DPRINT("Attaching FCB to fileObject\n");
Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, FileObject);
if (!NT_SUCCESS(Status))
{
vfatReleaseFCB(DeviceExt, Fcb);
}
return Status;
}
/*
* FUNCTION: Create or open a file
*/
static NTSTATUS
VfatCreateFile(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION Stack;
PFILE_OBJECT FileObject;
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_EXTENSION DeviceExt;
ULONG RequestedDisposition, RequestedOptions;
PVFATFCB pFcb = NULL;
PVFATFCB ParentFcb = NULL;
PVFATCCB pCcb = NULL;
PWCHAR c, last;
BOOLEAN PagingFileCreate;
BOOLEAN Dots;
BOOLEAN OpenTargetDir;
BOOLEAN TrailingBackslash;
UNICODE_STRING FileNameU;
UNICODE_STRING PathNameU;
ULONG Attributes;
/* Unpack the various parameters. */
Stack = IoGetCurrentIrpStackLocation(Irp);
RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
PagingFileCreate = BooleanFlagOn(Stack->Flags, SL_OPEN_PAGING_FILE);
OpenTargetDir = BooleanFlagOn(Stack->Flags, SL_OPEN_TARGET_DIRECTORY);
FileObject = Stack->FileObject;
DeviceExt = DeviceObject->DeviceExtension;
if (BooleanFlagOn(Stack->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID))
{
return STATUS_NOT_IMPLEMENTED;
}
/* Check their validity. */
if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
RequestedDisposition == FILE_SUPERSEDE)
{
return STATUS_INVALID_PARAMETER;
}
if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE))
{
return STATUS_INVALID_PARAMETER;
}
/* Deny create if the volume is locked */
if (BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED))
{
return STATUS_ACCESS_DENIED;
}
/* This a open operation for the volume itself */
if (FileObject->FileName.Length == 0 &&
(FileObject->RelatedFileObject == NULL ||
FileObject->RelatedFileObject->FsContext2 != NULL ||
FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb))
{
DPRINT("Volume opening\n");
if (RequestedDisposition != FILE_OPEN &&
RequestedDisposition != FILE_OPEN_IF)
{
return STATUS_ACCESS_DENIED;
}
if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
(FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 == NULL || FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb))
{
return STATUS_NOT_A_DIRECTORY;
}
if (OpenTargetDir)
{
return STATUS_INVALID_PARAMETER;
}
if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
{
return STATUS_CANNOT_DELETE;
}
vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
pFcb = DeviceExt->VolumeFcb;
if (pFcb->OpenHandleCount == 0)
{
IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
Stack->Parameters.Create.ShareAccess,
FileObject,
&pFcb->FCBShareAccess);
}
else
{
Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
Stack->Parameters.Create.ShareAccess,
FileObject,
&pFcb->FCBShareAccess,
FALSE);
if (!NT_SUCCESS(Status))
{
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return Status;
}
}
vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
DeviceExt->OpenHandleCount++;
pFcb->OpenHandleCount++;
vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
Irp->IoStatus.Information = FILE_OPENED;
return STATUS_SUCCESS;
}
if (FileObject->RelatedFileObject != NULL &&
FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb)
{
ASSERT(FileObject->FileName.Length != 0);
return STATUS_OBJECT_PATH_NOT_FOUND;
}
/* Check for illegal characters and illegal dot sequences in the file name */
PathNameU = FileObject->FileName;
c = PathNameU.Buffer + PathNameU.Length / sizeof(WCHAR);
last = c - 1;
Dots = TRUE;
while (c-- > PathNameU.Buffer)
{
if (*c == L'\\' || c == PathNameU.Buffer)
{
if (Dots && last > c)
{
return STATUS_OBJECT_NAME_INVALID;
}
if (*c == L'\\' && (c - 1) > PathNameU.Buffer &&
*(c - 1) == L'\\')
{
return STATUS_OBJECT_NAME_INVALID;
}
last = c - 1;
Dots = TRUE;
}
else if (*c != L'.')
{
Dots = FALSE;
}
if (*c != '\\' && vfatIsLongIllegal(*c))
{
return STATUS_OBJECT_NAME_INVALID;
}
}
/* Check if we try to open target directory of root dir */
if (OpenTargetDir && FileObject->RelatedFileObject == NULL && PathNameU.Length == sizeof(WCHAR) &&
PathNameU.Buffer[0] == L'\\')
{
return STATUS_INVALID_PARAMETER;
}
if (FileObject->RelatedFileObject && PathNameU.Length >= sizeof(WCHAR) && PathNameU.Buffer[0] == L'\\')
{
return STATUS_OBJECT_NAME_INVALID;
}
TrailingBackslash = FALSE;
if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\')
{
PathNameU.Length -= sizeof(WCHAR);
TrailingBackslash = TRUE;
}
if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\')
{
return STATUS_OBJECT_NAME_INVALID;
}
/* Try opening the file. */
if (!OpenTargetDir)
{
vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, RequestedOptions, &ParentFcb);
}
else
{
PVFATFCB TargetFcb;
LONG idx, FileNameLen;
vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
ParentFcb = (FileObject->RelatedFileObject != NULL) ? FileObject->RelatedFileObject->FsContext : NULL;
if (ParentFcb)
{
vfatGrabFCB(DeviceExt, ParentFcb);
}
Status = vfatGetFCBForFile(DeviceExt, &ParentFcb, &TargetFcb, &PathNameU);
if (NT_SUCCESS(Status))
{
vfatReleaseFCB(DeviceExt, TargetFcb);
Irp->IoStatus.Information = FILE_EXISTS;
}
else
{
Irp->IoStatus.Information = FILE_DOES_NOT_EXIST;
}
idx = FileObject->FileName.Length / sizeof(WCHAR) - 1;
/* Skip trailing \ - if any */
if (PathNameU.Buffer[idx] == L'\\')
{
--idx;
PathNameU.Length -= sizeof(WCHAR);
}
/* Get file name */
while (idx >= 0 && PathNameU.Buffer[idx] != L'\\')
{
--idx;
}
if (idx > 0 || PathNameU.Buffer[0] == L'\\')
{
/* We don't want to include / in the name */
FileNameLen = PathNameU.Length - ((idx + 1) * sizeof(WCHAR));
/* Update FO just to keep file name */
/* Skip first slash */
++idx;
FileObject->FileName.Length = FileNameLen;
RtlMoveMemory(&PathNameU.Buffer[0], &PathNameU.Buffer[idx], FileObject->FileName.Length);
#if 0
/* Terminate the string at the last backslash */
PathNameU.Buffer[idx + 1] = UNICODE_NULL;
PathNameU.Length = (idx + 1) * sizeof(WCHAR);
PathNameU.MaximumLength = PathNameU.Length + sizeof(WCHAR);
/* Update the file object as well */
FileObject->FileName.Length = PathNameU.Length;
FileObject->FileName.MaximumLength = PathNameU.MaximumLength;
#endif
}
else
{
/* This is a relative open and we have only the filename, so open the parent directory
* It is in RelatedFileObject
*/
ASSERT(FileObject->RelatedFileObject != NULL);
/* No need to modify the FO, it already has the name */
}
/* We're done with opening! */
if (ParentFcb != NULL)
{
Status = vfatAttachFCBToFileObject(DeviceExt, ParentFcb, FileObject);
}
if (NT_SUCCESS(Status))
{
pFcb = FileObject->FsContext;
ASSERT(pFcb == ParentFcb);
if (pFcb->OpenHandleCount == 0)
{
IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
Stack->Parameters.Create.ShareAccess,
FileObject,
&pFcb->FCBShareAccess);
}
else
{
Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
Stack->Parameters.Create.ShareAccess,
FileObject,
&pFcb->FCBShareAccess,
FALSE);
if (!NT_SUCCESS(Status))
{
VfatCloseFile(DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return Status;
}
}
pCcb = FileObject->FsContext2;
if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
{
pCcb->Flags |= CCB_DELETE_ON_CLOSE;
}
pFcb->OpenHandleCount++;
DeviceExt->OpenHandleCount++;
}
else if (ParentFcb != NULL)
{
vfatReleaseFCB(DeviceExt, ParentFcb);
}
if (NT_SUCCESS(Status))
{
vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
}
else
{
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
}
return Status;
}
/*
* If the directory containing the file to open doesn't exist then
* fail immediately
*/
if (Status == STATUS_OBJECT_PATH_NOT_FOUND ||
Status == STATUS_INVALID_PARAMETER ||
Status == STATUS_DELETE_PENDING ||
Status == STATUS_ACCESS_DENIED ||
Status == STATUS_OBJECT_NAME_COLLISION)
{
if (ParentFcb)
{
vfatReleaseFCB(DeviceExt, ParentFcb);
}
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return Status;
}
if (!NT_SUCCESS(Status) && ParentFcb == NULL)
{
DPRINT1("VfatOpenFile failed for '%wZ', status %x\n", &PathNameU, Status);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return Status;
}
Attributes = (Stack->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_ARCHIVE |
FILE_ATTRIBUTE_SYSTEM |
FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_DIRECTORY |
FILE_ATTRIBUTE_READONLY));
/* If the file open failed then create the required file */
if (!NT_SUCCESS (Status))
{
if (RequestedDisposition == FILE_CREATE ||
RequestedDisposition == FILE_OPEN_IF ||
RequestedDisposition == FILE_OVERWRITE_IF ||
RequestedDisposition == FILE_SUPERSEDE)
{
if (!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE))
{
if (TrailingBackslash)
{
vfatReleaseFCB(DeviceExt, ParentFcb);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return STATUS_OBJECT_NAME_INVALID;
}
Attributes |= FILE_ATTRIBUTE_ARCHIVE;
}
vfatSplitPathName(&PathNameU, NULL, &FileNameU);
Status = VfatAddEntry(DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions,
Attributes, NULL);
vfatReleaseFCB(DeviceExt, ParentFcb);
if (NT_SUCCESS(Status))
{
Status = vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
if (!NT_SUCCESS(Status))
{
vfatReleaseFCB(DeviceExt, pFcb);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return Status;
}
Irp->IoStatus.Information = FILE_CREATED;
VfatSetAllocationSizeInformation(FileObject,
pFcb,
DeviceExt,
&Irp->Overlay.AllocationSize);
VfatSetExtendedAttributes(FileObject,
Irp->AssociatedIrp.SystemBuffer,
Stack->Parameters.Create.EaLength);
if (PagingFileCreate)
{
pFcb->Flags |= FCB_IS_PAGE_FILE;
SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
}
}
else
{
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return Status;
}
}
else
{
vfatReleaseFCB(DeviceExt, ParentFcb);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return Status;
}
}
else
{
if (ParentFcb)
{
vfatReleaseFCB(DeviceExt, ParentFcb);
}
pFcb = FileObject->FsContext;
/* Otherwise fail if the caller wanted to create a new file */
if (RequestedDisposition == FILE_CREATE)
{
VfatCloseFile(DeviceExt, FileObject);
if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
{
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return STATUS_OBJECT_NAME_INVALID;
}
Irp->IoStatus.Information = FILE_EXISTS;
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return STATUS_OBJECT_NAME_COLLISION;
}
if (pFcb->OpenHandleCount != 0)
{
Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
Stack->Parameters.Create.ShareAccess,
FileObject,
&pFcb->FCBShareAccess,
FALSE);
if (!NT_SUCCESS(Status))
{
VfatCloseFile(DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return Status;
}
}
/*
* Check the file has the requested attributes
*/
if (BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE) &&
vfatFCBIsDirectory(pFcb))
{
VfatCloseFile (DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return STATUS_FILE_IS_A_DIRECTORY;
}
if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
!vfatFCBIsDirectory(pFcb))
{
VfatCloseFile (DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return STATUS_NOT_A_DIRECTORY;
}
if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
{
VfatCloseFile (DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return STATUS_OBJECT_NAME_INVALID;
}
#ifndef USE_ROS_CC_AND_FS
if (!vfatFCBIsDirectory(pFcb))
{
if (BooleanFlagOn(Stack->Parameters.Create.SecurityContext->DesiredAccess, FILE_WRITE_DATA) ||
RequestedDisposition == FILE_OVERWRITE ||
RequestedDisposition == FILE_OVERWRITE_IF ||
(RequestedOptions & FILE_DELETE_ON_CLOSE))
{
if (!MmFlushImageSection(&pFcb->SectionObjectPointers, MmFlushForWrite))
{
DPRINT1("%wZ\n", &pFcb->PathNameU);
DPRINT1("%d %d %d\n", Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA,
RequestedDisposition == FILE_OVERWRITE, RequestedDisposition == FILE_OVERWRITE_IF);
VfatCloseFile (DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) ? STATUS_CANNOT_DELETE
: STATUS_SHARING_VIOLATION;
}
}
}
#endif
if (PagingFileCreate)
{
/* FIXME:
* Do more checking for page files. It is possible,
* that the file was opened and closed previously
* as a normal cached file. In this case, the cache
* manager has referenced the fileobject and the fcb
* is held in memory. Try to remove the fileobject
* from cache manager and use the fcb.
*/
if (pFcb->RefCount > 1)
{
if(!BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
{
VfatCloseFile(DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return STATUS_INVALID_PARAMETER;
}
}
else
{
pFcb->Flags |= FCB_IS_PAGE_FILE;
SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
}
}
else
{
if (BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
{
VfatCloseFile(DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return STATUS_INVALID_PARAMETER;
}
}
if (RequestedDisposition == FILE_OVERWRITE ||
RequestedDisposition == FILE_OVERWRITE_IF ||
RequestedDisposition == FILE_SUPERSEDE)
{
if ((BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_HIDDEN) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_HIDDEN)) ||
(BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_SYSTEM) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_SYSTEM)))
{
VfatCloseFile(DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return STATUS_ACCESS_DENIED;
}
if (!vfatFCBIsDirectory(pFcb))
{
LARGE_INTEGER SystemTime;
if (RequestedDisposition == FILE_SUPERSEDE)
{
*pFcb->Attributes = Attributes;
}
else
{
*pFcb->Attributes |= Attributes;
}
*pFcb->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
KeQuerySystemTime(&SystemTime);
if (vfatVolumeIsFatX(DeviceExt))
{
FsdSystemTimeToDosDateTime(DeviceExt,
&SystemTime, &pFcb->entry.FatX.UpdateDate,
&pFcb->entry.FatX.UpdateTime);
}
else
{
FsdSystemTimeToDosDateTime(DeviceExt,
&SystemTime, &pFcb->entry.Fat.UpdateDate,
&pFcb->entry.Fat.UpdateTime);
}
VfatUpdateEntry(DeviceExt, pFcb);
}
ExAcquireResourceExclusiveLite(&(pFcb->MainResource), TRUE);
Status = VfatSetAllocationSizeInformation(FileObject,
pFcb,
DeviceExt,
&Irp->Overlay.AllocationSize);
ExReleaseResourceLite(&(pFcb->MainResource));
if (!NT_SUCCESS (Status))
{
VfatCloseFile(DeviceExt, FileObject);
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
return Status;
}
}
if (RequestedDisposition == FILE_SUPERSEDE)
{
Irp->IoStatus.Information = FILE_SUPERSEDED;
}
else if (RequestedDisposition == FILE_OVERWRITE ||
RequestedDisposition == FILE_OVERWRITE_IF)
{
Irp->IoStatus.Information = FILE_OVERWRITTEN;
}
else
{
Irp->IoStatus.Information = FILE_OPENED;
}
}
if (pFcb->OpenHandleCount == 0)
{
IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
Stack->Parameters.Create.ShareAccess,
FileObject,
&pFcb->FCBShareAccess);
}
else
{
IoUpdateShareAccess(FileObject,
&pFcb->FCBShareAccess);
}
pCcb = FileObject->FsContext2;
if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
{
pCcb->Flags |= CCB_DELETE_ON_CLOSE;
}
if (Irp->IoStatus.Information == FILE_CREATED)
{
vfatReportChange(DeviceExt,
pFcb,
(vfatFCBIsDirectory(pFcb) ?
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
FILE_ACTION_ADDED);
}
else if (Irp->IoStatus.Information == FILE_OVERWRITTEN ||
Irp->IoStatus.Information == FILE_SUPERSEDED)
{
vfatReportChange(DeviceExt,
pFcb,
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE,
FILE_ACTION_MODIFIED);
}
pFcb->OpenHandleCount++;
DeviceExt->OpenHandleCount++;
/* FIXME : test write access if requested */
/* FIXME: That is broken, we cannot reach this code path with failure */
ASSERT(NT_SUCCESS(Status));
if (NT_SUCCESS(Status))
{
vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
}
else
{
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
}
return Status;
}
/*
* FUNCTION: Create or open a file
*/
NTSTATUS
VfatCreate(
PVFAT_IRP_CONTEXT IrpContext)
{
NTSTATUS Status;
ASSERT(IrpContext);
if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
{
/* DeviceObject represents FileSystem instead of logical volume */
DPRINT ("FsdCreate called with file system\n");
IrpContext->Irp->IoStatus.Information = FILE_OPENED;
IrpContext->PriorityBoost = IO_DISK_INCREMENT;
return STATUS_SUCCESS;
}
IrpContext->Irp->IoStatus.Information = 0;
ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE);
Status = VfatCreateFile(IrpContext->DeviceObject, IrpContext->Irp);
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
if (NT_SUCCESS(Status))
IrpContext->PriorityBoost = IO_DISK_INCREMENT;
return Status;
}
/* EOF */