/* * PROJECT: ReactOS FAT file system driver * LICENSE: GNU GPLv3 as published by the Free Software Foundation * FILE: drivers/filesystems/fastfat/create.c * PURPOSE: Create routines * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org) */ /* INCLUDES *****************************************************************/ #define NDEBUG #include "fastfat.h" /* FUNCTIONS *****************************************************************/ IO_STATUS_BLOCK NTAPI FatiOpenRootDcb(IN PFAT_IRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, IN PVCB Vcb, IN PACCESS_MASK DesiredAccess, IN USHORT ShareAccess, IN ULONG CreateDisposition) { IO_STATUS_BLOCK Iosb; PFCB Dcb; NTSTATUS Status; PCCB Ccb; /* Reference our DCB */ Dcb = Vcb->RootDcb; DPRINT("Opening root directory\n"); /* Exclusively lock this DCB */ (VOID)FatAcquireExclusiveFcb(IrpContext, Dcb); do { /* Validate parameters */ if (CreateDisposition != FILE_OPEN && CreateDisposition != FILE_OPEN_IF) { Iosb.Status = STATUS_ACCESS_DENIED; break; } // TODO: Check file access /* Is it a first time open? */ if (Dcb->OpenCount == 0) { /* Set share access */ IoSetShareAccess(*DesiredAccess, ShareAccess, FileObject, &Dcb->ShareAccess); } else { /* Check share access */ Status = IoCheckShareAccess(*DesiredAccess, ShareAccess, FileObject, &Dcb->ShareAccess, TRUE); } /* Set file object pointers */ Ccb = FatCreateCcb(); FatSetFileObject(FileObject, UserDirectoryOpen, Dcb, Ccb); /* Increment counters */ Dcb->OpenCount++; Dcb->UncleanCount++; Vcb->OpenFileCount++; if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; /* Set success statuses */ Iosb.Status = STATUS_SUCCESS; Iosb.Information = FILE_OPENED; } while (FALSE); /* Release the DCB lock */ FatReleaseFcb(IrpContext, Dcb); return Iosb; } FF_ERROR NTAPI FatiTryToOpen(IN PFILE_OBJECT FileObject, IN PVCB Vcb) { OEM_STRING AnsiName; CHAR AnsiNameBuf[512]; FF_ERROR Error; NTSTATUS Status; FF_FILE *FileHandle; /* Convert the name to ANSI */ AnsiName.Buffer = AnsiNameBuf; AnsiName.Length = 0; AnsiName.MaximumLength = sizeof(AnsiNameBuf); RtlZeroMemory(AnsiNameBuf, sizeof(AnsiNameBuf)); Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiName, &FileObject->FileName, FALSE); if (!NT_SUCCESS(Status)) { ASSERT(FALSE); } /* Open the file with FullFAT */ FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_READ, &Error); /* Close the handle */ if (FileHandle) FF_Close(FileHandle); /* Return status */ return Error; } IO_STATUS_BLOCK NTAPI FatiOverwriteFile(PFAT_IRP_CONTEXT IrpContext, PFILE_OBJECT FileObject, PFCB Fcb, ULONG AllocationSize, PFILE_FULL_EA_INFORMATION EaBuffer, ULONG EaLength, UCHAR FileAttributes, ULONG CreateDisposition, BOOLEAN NoEaKnowledge) { IO_STATUS_BLOCK Iosb = {{0}}; PCCB Ccb; LARGE_INTEGER Zero; ULONG NotifyFilter; Zero.QuadPart = 0; /* Check Ea mismatch first */ if (NoEaKnowledge && EaLength > 0) { Iosb.Status = STATUS_ACCESS_DENIED; return Iosb; } do { /* Check if it's not still mapped */ if (!MmCanFileBeTruncated(&Fcb->SectionObjectPointers, &Zero)) { /* Fail */ Iosb.Status = STATUS_USER_MAPPED_FILE; break; } /* Set file object pointers */ Ccb = FatCreateCcb(); FatSetFileObject(FileObject, UserFileOpen, Fcb, Ccb); FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; /* Indicate that create is in progress */ Fcb->Vcb->State |= VCB_STATE_CREATE_IN_PROGRESS; /* Purge the cache section */ CcPurgeCacheSection(&Fcb->SectionObjectPointers, NULL, 0, FALSE); /* Add Eas */ if (EaLength > 0) { ASSERT(FALSE); } /* Acquire the paging resource */ (VOID)ExAcquireResourceExclusiveLite(Fcb->Header.PagingIoResource, TRUE); /* Initialize FCB header */ Fcb->Header.FileSize.QuadPart = 0; Fcb->Header.ValidDataLength.QuadPart = 0; /* Let CC know about changed file size */ CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize); // TODO: Actually truncate the file DPRINT1("TODO: Actually truncate file '%wZ' with a fullfat handle %x\n", &Fcb->FullFileName, Fcb->FatHandle); /* Release the paging resource */ ExReleaseResourceLite(Fcb->Header.PagingIoResource); /* Specify truncate on close */ Fcb->State |= FCB_STATE_TRUNCATE_ON_CLOSE; // TODO: Delete previous EA if needed /* Send notification about changes */ NotifyFilter = FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE; FsRtlNotifyFullReportChange(Fcb->Vcb->NotifySync, &Fcb->Vcb->NotifyList, (PSTRING)&Fcb->FullFileName, Fcb->FullFileName.Length - Fcb->FileNameLength, NULL, NULL, NotifyFilter, FILE_ACTION_MODIFIED, NULL); /* Set success status */ Iosb.Status = STATUS_SUCCESS; /* Set correct information code */ Iosb.Information = (CreateDisposition == FILE_SUPERSEDE) ? FILE_SUPERSEDED : FILE_OVERWRITTEN; } while (0); /* Remove the create in progress flag */ ClearFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS); return Iosb; } IO_STATUS_BLOCK NTAPI FatiOpenExistingDir(IN PFAT_IRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, IN PVCB Vcb, IN PFCB ParentDcb, IN PACCESS_MASK DesiredAccess, IN USHORT ShareAccess, IN ULONG AllocationSize, IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, IN UCHAR FileAttributes, IN ULONG CreateDisposition, IN BOOLEAN DeleteOnClose) { IO_STATUS_BLOCK Iosb = {{0}}; OEM_STRING AnsiName; CHAR AnsiNameBuf[512]; PFCB Fcb; NTSTATUS Status; FF_FILE *FileHandle; /* Only open is permitted */ if (CreateDisposition != FILE_OPEN && CreateDisposition != FILE_OPEN_IF) { Iosb.Status = STATUS_OBJECT_NAME_COLLISION; return Iosb; } // TODO: Check dir access /* Convert the name to ANSI */ AnsiName.Buffer = AnsiNameBuf; AnsiName.Length = 0; AnsiName.MaximumLength = sizeof(AnsiNameBuf); RtlZeroMemory(AnsiNameBuf, sizeof(AnsiNameBuf)); Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiName, &FileObject->FileName, FALSE); if (!NT_SUCCESS(Status)) { ASSERT(FALSE); } /* Open the dir with FullFAT */ FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_DIR, NULL); if (!FileHandle) { Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND; // FIXME: A shortcut for now return Iosb; } /* Create a new DCB for this directory */ Fcb = FatCreateDcb(IrpContext, Vcb, ParentDcb, FileHandle); /* Set share access */ IoSetShareAccess(*DesiredAccess, ShareAccess, FileObject, &Fcb->ShareAccess); /* Set context and section object pointers */ FatSetFileObject(FileObject, UserDirectoryOpen, Fcb, FatCreateCcb()); /* Increase counters */ Fcb->UncleanCount++; Fcb->OpenCount++; Vcb->OpenFileCount++; if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; Iosb.Status = STATUS_SUCCESS; Iosb.Information = FILE_OPENED; DPRINT1("Successfully opened dir %s\n", AnsiNameBuf); return Iosb; } IO_STATUS_BLOCK NTAPI FatiOpenExistingFile(IN PFAT_IRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, IN PVCB Vcb, IN PFCB ParentDcb, IN PACCESS_MASK DesiredAccess, IN USHORT ShareAccess, IN ULONG AllocationSize, IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, IN UCHAR FileAttributes, IN ULONG CreateDisposition, IN BOOLEAN IsPagingFile, IN BOOLEAN DeleteOnClose, IN BOOLEAN IsDosName) { IO_STATUS_BLOCK Iosb = {{0}}; OEM_STRING AnsiName; CHAR AnsiNameBuf[512]; PFCB Fcb; NTSTATUS Status; FF_FILE *FileHandle; FF_ERROR FfError; /* Check for create file option and fail */ if (CreateDisposition == FILE_CREATE) { Iosb.Status = STATUS_OBJECT_NAME_COLLISION; return Iosb; } // TODO: Check more params /* Convert the name to ANSI */ AnsiName.Buffer = AnsiNameBuf; AnsiName.Length = 0; AnsiName.MaximumLength = sizeof(AnsiNameBuf); RtlZeroMemory(AnsiNameBuf, sizeof(AnsiNameBuf)); Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiName, &FileObject->FileName, FALSE); if (!NT_SUCCESS(Status)) { ASSERT(FALSE); } /* Open the file with FullFAT */ FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_READ, &FfError); if (!FileHandle) { DPRINT1("Failed to open file '%s', error %ld\n", AnsiName.Buffer, FfError); Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND; // FIXME: A shortcut for now return Iosb; } DPRINT1("Succeeded opening file '%s'\n", AnsiName.Buffer); /* Create a new FCB for this file */ Fcb = FatCreateFcb(IrpContext, Vcb, ParentDcb, FileHandle); // TODO: Check if overwrite is needed // TODO: This is usual file open branch, without overwriting! /* Set context and section object pointers */ FatSetFileObject(FileObject, UserFileOpen, Fcb, FatCreateCcb()); FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; Iosb.Status = STATUS_SUCCESS; Iosb.Information = FILE_OPENED; /* Increase counters */ Fcb->UncleanCount++; Fcb->OpenCount++; if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) Fcb->NonCachedUncleanCount++; if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; return Iosb; } IO_STATUS_BLOCK NTAPI FatiOpenVolume(IN PFAT_IRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, IN PVCB Vcb, IN PACCESS_MASK DesiredAccess, IN USHORT ShareAccess, IN ULONG CreateDisposition) { PCCB Ccb; IO_STATUS_BLOCK Iosb = {{0}}; BOOLEAN VolumeFlushed = FALSE; /* Check parameters */ if (CreateDisposition != FILE_OPEN && CreateDisposition != FILE_OPEN_IF) { /* Deny access */ Iosb.Status = STATUS_ACCESS_DENIED; } /* Check if it's exclusive open */ if (!FlagOn(ShareAccess, FILE_SHARE_WRITE) && !FlagOn(ShareAccess, FILE_SHARE_DELETE)) { // TODO: Check if exclusive read access requested // and opened handles count is not 0 //if (!FlagOn(ShareAccess, FILE_SHARE_READ) DPRINT1("Exclusive volume open\n"); // TODO: Flush the volume VolumeFlushed = TRUE; } else if (FlagOn(*DesiredAccess, FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA)) { DPRINT1("Shared open\n"); // TODO: Flush the volume VolumeFlushed = TRUE; } if (VolumeFlushed && !FlagOn(Vcb->State, VCB_STATE_MOUNTED_DIRTY) && FlagOn(Vcb->State, VCB_STATE_FLAG_DIRTY) && CcIsThereDirtyData(Vcb->Vpb)) { UNIMPLEMENTED; } /* Set share access */ if (Vcb->DirectOpenCount > 0) { /* This volume has already been opened */ Iosb.Status = IoCheckShareAccess(*DesiredAccess, ShareAccess, FileObject, &Vcb->ShareAccess, TRUE); if (!NT_SUCCESS(Iosb.Status)) { ASSERT(FALSE); } } else { /* This is the first time open */ IoSetShareAccess(*DesiredAccess, ShareAccess, FileObject, &Vcb->ShareAccess); } /* Set file object pointers */ Ccb = FatCreateCcb(); FatSetFileObject(FileObject, UserVolumeOpen, Vcb, Ccb); FileObject->SectionObjectPointer = &Vcb->SectionObjectPointers; /* Increase direct open count */ Vcb->DirectOpenCount++; Vcb->OpenFileCount++; if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; /* Set no buffering flag */ FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING; // TODO: User's access check Iosb.Status = STATUS_SUCCESS; Iosb.Information = FILE_OPENED; return Iosb; } NTSTATUS NTAPI FatiCreate(IN PFAT_IRP_CONTEXT IrpContext, IN PIRP Irp) { /* Boolean options */ BOOLEAN CreateDirectory; BOOLEAN SequentialOnly; BOOLEAN NoIntermediateBuffering; BOOLEAN OpenDirectory; BOOLEAN IsPagingFile; BOOLEAN OpenTargetDirectory; BOOLEAN IsDirectoryFile; BOOLEAN NonDirectoryFile; BOOLEAN NoEaKnowledge; BOOLEAN DeleteOnClose; BOOLEAN TemporaryFile; ULONG CreateDisposition; /* Control blocks */ PVCB Vcb, DecodedVcb, RelatedVcb; PFCB Fcb, NextFcb, RelatedDcb; PCCB Ccb, RelatedCcb; PFCB ParentDcb; /* IRP data */ PFILE_OBJECT FileObject; PFILE_OBJECT RelatedFO; UNICODE_STRING FileName; ULONG AllocationSize; PFILE_FULL_EA_INFORMATION EaBuffer; PACCESS_MASK DesiredAccess; ULONG Options; UCHAR FileAttributes; USHORT ShareAccess; ULONG EaLength; /* Misc */ NTSTATUS Status; IO_STATUS_BLOCK Iosb; PIO_STACK_LOCATION IrpSp; BOOLEAN EndBackslash = FALSE, OpenedAsDos, FirstRun = TRUE; UNICODE_STRING RemainingPart, FirstName, NextName, FileNameUpcased; OEM_STRING AnsiFirstName; FF_ERROR FfError; TYPE_OF_OPEN TypeOfOpen; BOOLEAN OplockPostIrp = FALSE; Iosb.Status = STATUS_SUCCESS; /* Get current IRP stack location */ IrpSp = IoGetCurrentIrpStackLocation(Irp); DPRINT("FatCommonCreate\n", 0 ); DPRINT("Irp = %08lx\n", Irp ); DPRINT("\t->Flags = %08lx\n", Irp->Flags ); DPRINT("\t->FileObject = %08lx\n", IrpSp->FileObject ); DPRINT("\t->RelatedFileObject = %08lx\n", IrpSp->FileObject->RelatedFileObject ); DPRINT("\t->FileName = %wZ\n", &IrpSp->FileObject->FileName ); DPRINT("\t->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart ); DPRINT("\t->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart ); DPRINT("\t->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer ); DPRINT("\t->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); DPRINT("\t->Options = %08lx\n", IrpSp->Parameters.Create.Options ); DPRINT("\t->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes ); DPRINT("\t->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess ); DPRINT("\t->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength ); /* Apply a special hack for Win32, idea taken from FASTFAT reference driver from WDK */ if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) && (IrpSp->FileObject->FileName.Buffer[1] == L'\\') && (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) { /* Remove a leading slash */ IrpSp->FileObject->FileName.Length -= sizeof(WCHAR); RtlMoveMemory(&IrpSp->FileObject->FileName.Buffer[0], &IrpSp->FileObject->FileName.Buffer[1], IrpSp->FileObject->FileName.Length ); /* Check again: if there are still two leading slashes, exit with an error */ if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) && (IrpSp->FileObject->FileName.Buffer[1] == L'\\') && (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) { FatCompleteRequest( IrpContext, Irp, STATUS_OBJECT_NAME_INVALID ); DPRINT1("FatiCreate: STATUS_OBJECT_NAME_INVALID\n"); return STATUS_OBJECT_NAME_INVALID; } } /* Make sure we have SecurityContext */ ASSERT(IrpSp->Parameters.Create.SecurityContext != NULL); /* Get necessary data out of IRP */ FileObject = IrpSp->FileObject; FileName = FileObject->FileName; RelatedFO = FileObject->RelatedFileObject; AllocationSize = Irp->Overlay.AllocationSize.LowPart; EaBuffer = Irp->AssociatedIrp.SystemBuffer; DesiredAccess = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess; Options = IrpSp->Parameters.Create.Options; FileAttributes = (UCHAR)(IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL); ShareAccess = IrpSp->Parameters.Create.ShareAccess; EaLength = IrpSp->Parameters.Create.EaLength; /* Set VPB to related object's VPB if it exists */ if (RelatedFO) FileObject->Vpb = RelatedFO->Vpb; /* Reject open by id */ if (Options & FILE_OPEN_BY_FILE_ID) { FatCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER); return STATUS_INVALID_PARAMETER; } /* Prepare file attributes mask */ FileAttributes &= (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE); /* Get the volume control object */ Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb; /* Get options */ IsDirectoryFile = BooleanFlagOn(Options, FILE_DIRECTORY_FILE); NonDirectoryFile = BooleanFlagOn(Options, FILE_NON_DIRECTORY_FILE); SequentialOnly = BooleanFlagOn(Options, FILE_SEQUENTIAL_ONLY); NoIntermediateBuffering = BooleanFlagOn(Options, FILE_NO_INTERMEDIATE_BUFFERING); NoEaKnowledge = BooleanFlagOn(Options, FILE_NO_EA_KNOWLEDGE); DeleteOnClose = BooleanFlagOn(Options, FILE_DELETE_ON_CLOSE); TemporaryFile = BooleanFlagOn(IrpSp->Parameters.Create.FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); IsPagingFile = BooleanFlagOn(IrpSp->Flags, SL_OPEN_PAGING_FILE); OpenTargetDirectory = BooleanFlagOn(IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY); /* Calculate create disposition */ CreateDisposition = (Options >> 24) & 0x000000ff; /* Get Create/Open directory flags based on it */ CreateDirectory = (BOOLEAN)(IsDirectoryFile && ((CreateDisposition == FILE_CREATE) || (CreateDisposition == FILE_OPEN_IF))); OpenDirectory = (BOOLEAN)(IsDirectoryFile && ((CreateDisposition == FILE_OPEN) || (CreateDisposition == FILE_OPEN_IF))); /* Validate parameters: directory/nondirectory mismatch and AllocationSize being more than 4GB */ if ((IsDirectoryFile && NonDirectoryFile) || Irp->Overlay.AllocationSize.HighPart != 0) { FatCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER); DPRINT1("FatiCreate: STATUS_INVALID_PARAMETER\n", 0); return STATUS_INVALID_PARAMETER; } /* Acquire the VCB lock exclusively */ if (!FatAcquireExclusiveVcb(IrpContext, Vcb)) { // TODO: Postpone the IRP for later processing ASSERT(FALSE); return STATUS_NOT_IMPLEMENTED; } // TODO: Verify the VCB /* If VCB is locked, then no file openings are possible */ if (Vcb->State & VCB_STATE_FLAG_LOCKED) { DPRINT1("This volume is locked\n"); Status = STATUS_ACCESS_DENIED; /* Set volume dismount status */ if (Vcb->Condition != VcbGood) Status = STATUS_VOLUME_DISMOUNTED; /* Cleanup and return */ FatReleaseVcb(IrpContext, Vcb); FatCompleteRequest(IrpContext, Irp, Status); return Status; } /* Check if the volume is write protected and disallow DELETE_ON_CLOSE */ if (DeleteOnClose & FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED)) { ASSERT(FALSE); return STATUS_NOT_IMPLEMENTED; } // TODO: Make sure EAs aren't supported on FAT32 /* Check if it's a volume open request */ if (FileName.Length == 0) { /* Test related FO to be sure */ if (!RelatedFO || FatDecodeFileObject(RelatedFO, &DecodedVcb, &Fcb, &Ccb) == UserVolumeOpen) { /* Check parameters */ if (IsDirectoryFile || OpenTargetDirectory) { Status = IsDirectoryFile ? STATUS_NOT_A_DIRECTORY : STATUS_INVALID_PARAMETER; /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request and return */ FatCompleteRequest(IrpContext, Irp, Status); return Status; } /* It is indeed a volume open request */ Iosb = FatiOpenVolume(IrpContext, FileObject, Vcb, DesiredAccess, ShareAccess, CreateDisposition); /* Set resulting information */ Irp->IoStatus.Information = Iosb.Information; /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request and return */ FatCompleteRequest(IrpContext, Irp, Iosb.Status); return Iosb.Status; } } /* Check if this is a relative open */ if (RelatedFO) { /* Decode the file object */ TypeOfOpen = FatDecodeFileObject(RelatedFO, &RelatedVcb, &RelatedDcb, &RelatedCcb); /* Check open type */ if (TypeOfOpen != UserFileOpen && TypeOfOpen != UserDirectoryOpen) { DPRINT1("Invalid file object!\n"); Status = STATUS_OBJECT_PATH_NOT_FOUND; /* Cleanup and return */ FatReleaseVcb(IrpContext, Vcb); FatCompleteRequest(IrpContext, Irp, Status); return Status; } /* File path must be relative */ if (FileName.Length != 0 && FileName.Buffer[0] == L'\\') { Status = STATUS_OBJECT_NAME_INVALID; /* The name is absolute, fail */ FatReleaseVcb(IrpContext, Vcb); FatCompleteRequest(IrpContext, Irp, Status); return Status; } /* Make sure volume is the same */ ASSERT(RelatedVcb == Vcb); /* Save VPB */ FileObject->Vpb = RelatedFO->Vpb; /* Set parent DCB */ ParentDcb = RelatedDcb; DPRINT("Opening file '%wZ' relatively to '%wZ'\n", &FileName, &ParentDcb->FullFileName); } else { /* Absolute open */ if ((FileName.Length == sizeof(WCHAR)) && (FileName.Buffer[0] == L'\\')) { /* Check if it's ok to open it */ if (NonDirectoryFile) { DPRINT1("Trying to open root dir as a file\n"); Status = STATUS_FILE_IS_A_DIRECTORY; /* Cleanup and return */ FatReleaseVcb(IrpContext, Vcb); FatCompleteRequest(IrpContext, Irp, Status); return Status; } /* Check for target directory on a root dir */ if (OpenTargetDirectory) { Status = STATUS_INVALID_PARAMETER; /* Cleanup and return */ FatReleaseVcb(IrpContext, Vcb); FatCompleteRequest(IrpContext, Irp, Status); return Status; } /* Check delete on close on a root dir */ if (DeleteOnClose) { Status = STATUS_CANNOT_DELETE; /* Cleanup and return */ FatReleaseVcb(IrpContext, Vcb); FatCompleteRequest(IrpContext, Irp, Status); return Status; } /* Call root directory open routine */ Iosb = FatiOpenRootDcb(IrpContext, FileObject, Vcb, DesiredAccess, ShareAccess, CreateDisposition); Irp->IoStatus.Information = Iosb.Information; /* Cleanup and return */ FatReleaseVcb(IrpContext, Vcb); FatCompleteRequest(IrpContext, Irp, Iosb.Status); return Iosb.Status; } else { /* Not a root dir */ ParentDcb = Vcb->RootDcb; DPRINT("ParentDcb %p\n", ParentDcb); } } /* Check for backslash at the end */ if (FileName.Length && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == L'\\') { /* Cut it out */ FileName.Length -= sizeof(WCHAR); /* Remember we cut it */ EndBackslash = TRUE; } /* Ensure the name is set */ if (!ParentDcb->FullFileName.Buffer) { /* Set it if it's missing */ FatSetFullFileNameInFcb(IrpContext, ParentDcb); } /* Check max path length */ if (ParentDcb->FullFileName.Length + FileName.Length + sizeof(WCHAR) <= FileName.Length) { DPRINT1("Max length is way off\n"); Iosb.Status = STATUS_OBJECT_NAME_INVALID; ASSERT(FALSE); } /* Loop through FCBs to find a good one */ while (TRUE) { Fcb = ParentDcb; /* Dissect the name */ RemainingPart = FileName; while (RemainingPart.Length) { FsRtlDissectName(RemainingPart, &FirstName, &NextName); /* Check for validity */ if ((NextName.Length && NextName.Buffer[0] == L'\\') || (NextName.Length > 255 * sizeof(WCHAR))) { /* The name is invalid */ DPRINT1("Invalid name found\n"); Iosb.Status = STATUS_OBJECT_NAME_INVALID; ASSERT(FALSE); } /* Convert the name to ANSI */ AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length); AnsiFirstName.Length = 0; AnsiFirstName.MaximumLength = FirstName.Length; Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE); if (!NT_SUCCESS(Status)) { DPRINT1("RtlUpcaseUnicodeStringToCountedOemString() failed with 0x%08x\n", Status); ASSERT(FALSE); NextFcb = NULL; AnsiFirstName.Length = 0; } else { /* Find the coresponding FCB */ NextFcb = FatFindFcb(IrpContext, &Fcb->Dcb.SplayLinksAnsi, (PSTRING)&AnsiFirstName, &OpenedAsDos); } /* If nothing found - try with unicode */ if (!NextFcb && Fcb->Dcb.SplayLinksUnicode) { FileNameUpcased.Buffer = FsRtlAllocatePool(PagedPool, FirstName.Length); FileNameUpcased.Length = 0; FileNameUpcased.MaximumLength = FirstName.Length; /* Downcase and then upcase to normalize it */ Status = RtlDowncaseUnicodeString(&FileNameUpcased, &FirstName, FALSE); Status = RtlUpcaseUnicodeString(&FileNameUpcased, &FileNameUpcased, FALSE); /* Try to find FCB again using unicode name */ NextFcb = FatFindFcb(IrpContext, &Fcb->Dcb.SplayLinksUnicode, (PSTRING)&FileNameUpcased, &OpenedAsDos); } /* Move to the next FCB */ if (NextFcb) { Fcb = NextFcb; RemainingPart = NextName; } /* Break out of this loop if nothing can be found */ if (!NextFcb || NextName.Length == 0 || FatNodeType(NextFcb) == FAT_NTC_FCB) { break; } } /* Ensure remaining name doesn't start from a backslash */ if (RemainingPart.Length && RemainingPart.Buffer[0] == L'\\') { /* Cut it */ RemainingPart.Buffer++; RemainingPart.Length -= sizeof(WCHAR); } if (Fcb->Condition == FcbGood) { /* Good FCB, break out of the loop */ break; } else { ASSERT(FALSE); } } /* Treat page file in a special way */ if (IsPagingFile) { UNIMPLEMENTED; // FIXME: System file too } /* Make sure there is no pending delete on a higher-level FCB */ if (Fcb->State & FCB_STATE_DELETE_ON_CLOSE) { Iosb.Status = STATUS_DELETE_PENDING; /* Cleanup and return */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request */ FatCompleteRequest(IrpContext, Irp, Iosb.Status); return Iosb.Status; } /* We have a valid FCB now */ if (!RemainingPart.Length) { /* Check for target dir open */ if (OpenTargetDirectory) { DPRINT1("Opening target dir is missing\n"); ASSERT(FALSE); } /* Check this FCB's type */ if (FatNodeType(Fcb) == FAT_NTC_ROOT_DCB || FatNodeType(Fcb) == FAT_NTC_DCB) { /* Open a directory */ if (NonDirectoryFile) { /* Forbidden */ Iosb.Status = STATUS_FILE_IS_A_DIRECTORY; ASSERT(FALSE); return Iosb.Status; } /* Open existing DCB */ Iosb = FatiOpenExistingDcb(IrpContext, FileObject, Vcb, Fcb, DesiredAccess, ShareAccess, CreateDisposition, NoEaKnowledge, DeleteOnClose); /* Save information */ Irp->IoStatus.Information = Iosb.Information; /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request */ FatCompleteRequest(IrpContext, Irp, Iosb.Status); return Iosb.Status; } else if (FatNodeType(Fcb) == FAT_NTC_FCB) { /* Open a file */ if (OpenDirectory) { /* Forbidden */ Iosb.Status = STATUS_NOT_A_DIRECTORY; ASSERT(FALSE); return Iosb.Status; } /* Check for trailing backslash */ if (EndBackslash) { /* Forbidden */ Iosb.Status = STATUS_OBJECT_NAME_INVALID; ASSERT(FALSE); return Iosb.Status; } Iosb = FatiOpenExistingFcb(IrpContext, FileObject, Vcb, Fcb, DesiredAccess, ShareAccess, AllocationSize, EaBuffer, EaLength, FileAttributes, CreateDisposition, NoEaKnowledge, DeleteOnClose, OpenedAsDos, &OplockPostIrp); /* Check if it's pending */ if (Iosb.Status != STATUS_PENDING) { /* In case of success set cache supported flag */ if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) { SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED); } /* Save information */ Irp->IoStatus.Information = Iosb.Information; /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request */ FatCompleteRequest(IrpContext, Irp, Iosb.Status); return Iosb.Status; } else { /* Queue this IRP */ UNIMPLEMENTED; ASSERT(FALSE); } } else { /* Unexpected FCB type */ KeBugCheckEx(FAT_FILE_SYSTEM, __LINE__, (ULONG_PTR)Fcb, 0, 0); } } /* During parsing we encountered a part which has no attached FCB/DCB. Check that the parent is really DCB and not FCB */ if (FatNodeType(Fcb) != FAT_NTC_ROOT_DCB && FatNodeType(Fcb) != FAT_NTC_DCB) { DPRINT1("Weird FCB node type %x, expected DCB or root DCB\n", FatNodeType(Fcb)); ASSERT(FALSE); } /* Create additional DCBs for all path items */ ParentDcb = Fcb; while (TRUE) { if (FirstRun) { RemainingPart = NextName; if (AnsiFirstName.Length) Status = STATUS_SUCCESS; else Status = STATUS_UNMAPPABLE_CHARACTER; /* First run init is done */ FirstRun = FALSE; } else { FsRtlDissectName(RemainingPart, &FirstName, &RemainingPart); /* Check for validity */ if ((RemainingPart.Length && RemainingPart.Buffer[0] == L'\\') || (NextName.Length > 255 * sizeof(WCHAR))) { /* The name is invalid */ DPRINT1("Invalid name found\n"); Iosb.Status = STATUS_OBJECT_NAME_INVALID; ASSERT(FALSE); } /* Convert the name to ANSI */ AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length); AnsiFirstName.Length = 0; AnsiFirstName.MaximumLength = FirstName.Length; Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE); } if (!NT_SUCCESS(Status)) { ASSERT(FALSE); } DPRINT("FirstName %wZ, RemainingPart %wZ\n", &FirstName, &RemainingPart); /* Break if came to the end */ if (!RemainingPart.Length) break; /* Create a DCB for this entry */ ParentDcb = FatCreateDcb(IrpContext, Vcb, ParentDcb, NULL); /* Set its name */ FatSetFullNameInFcb(ParentDcb, &FirstName); } /* Try to open it and get a result, saying if this is a dir or a file */ FfError = FatiTryToOpen(FileObject, Vcb); /* Check if we need to open target directory */ if (OpenTargetDirectory) { // TODO: Open target directory UNIMPLEMENTED; } /* Check, if path is a existing directory or file */ if (FfError == FF_ERR_FILE_OBJECT_IS_A_DIR || FfError == FF_ERR_FILE_ALREADY_OPEN || FfError == FF_ERR_NONE) { if (FfError == FF_ERR_FILE_OBJECT_IS_A_DIR) { if (NonDirectoryFile) { DPRINT1("Can't open dir as a file\n"); /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request */ Iosb.Status = STATUS_FILE_IS_A_DIRECTORY; FatCompleteRequest(IrpContext, Irp, Iosb.Status); return Iosb.Status; } /* Open this directory */ Iosb = FatiOpenExistingDir(IrpContext, FileObject, Vcb, ParentDcb, DesiredAccess, ShareAccess, AllocationSize, EaBuffer, EaLength, FileAttributes, CreateDisposition, DeleteOnClose); Irp->IoStatus.Information = Iosb.Information; /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request */ FatCompleteRequest(IrpContext, Irp, Iosb.Status); return Iosb.Status; } else { /* This is opening an existing file */ if (OpenDirectory) { /* But caller wanted a dir */ Status = STATUS_NOT_A_DIRECTORY; /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request */ FatCompleteRequest(IrpContext, Irp, Status); return Status; } /* If end backslash here, then it's definately not permitted, since we're opening files here */ if (EndBackslash) { /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request */ Iosb.Status = STATUS_OBJECT_NAME_INVALID; FatCompleteRequest(IrpContext, Irp, Iosb.Status); return Iosb.Status; } /* Try to open the file */ Iosb = FatiOpenExistingFile(IrpContext, FileObject, Vcb, ParentDcb, DesiredAccess, ShareAccess, AllocationSize, EaBuffer, EaLength, FileAttributes, CreateDisposition, FALSE, DeleteOnClose, OpenedAsDos); /* In case of success set cache supported flag */ if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) { SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED); } Irp->IoStatus.Information = Iosb.Information; /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request */ FatCompleteRequest(IrpContext, Irp, Iosb.Status); return Iosb.Status; } } /* We come here only in the case when a new file is created */ //ASSERT(FALSE); DPRINT1("TODO: Create a new file/directory, called '%wZ'\n", &IrpSp->FileObject->FileName); Status = STATUS_NOT_IMPLEMENTED; /* Unlock VCB */ FatReleaseVcb(IrpContext, Vcb); /* Complete the request */ FatCompleteRequest(IrpContext, Irp, Status); return Status; } NTSTATUS NTAPI FatCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) { PFAT_IRP_CONTEXT IrpContext; NTSTATUS Status; /* If it's called with our Disk FS device object - it's always open */ // TODO: Add check for CDROM FS device object if (DeviceObject == FatGlobalData.DiskDeviceObject) { /* Complete the request and return success */ Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest(Irp, IO_DISK_INCREMENT); return STATUS_SUCCESS; } /* Enter FsRtl critical region */ FsRtlEnterFileSystem(); /* Build an irp context */ IrpContext = FatBuildIrpContext(Irp, TRUE); /* Call internal function */ Status = FatiCreate(IrpContext, Irp); /* Leave FsRtl critical region */ FsRtlExitFileSystem(); return Status; } /* EOF */