/* * 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/fs/vfat/create.c * PURPOSE: VFAT Filesystem * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com) */ /* INCLUDES *****************************************************************/ #define NDEBUG #include "vfat.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 (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 (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); } NTSTATUS ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt, PVPB Vpb) /* * FUNCTION: Read the volume label */ { PVOID Context = NULL; ULONG DirIndex = 0; PDIR_ENTRY Entry; PVFATFCB pFcb; LARGE_INTEGER FileOffset; UNICODE_STRING NameU; ULONG SizeDirEntry; ULONG EntriesPerPage; OEM_STRING StringO; NameU.Buffer = Vpb->VolumeLabel; NameU.Length = 0; NameU.MaximumLength = sizeof(Vpb->VolumeLabel); *(Vpb->VolumeLabel) = 0; Vpb->VolumeLabelLength = 0; if (DeviceExt->Flags & VCB_IS_FATX) { SizeDirEntry = sizeof(FATX_DIR_ENTRY); EntriesPerPage = FATX_ENTRIES_PER_PAGE; } else { SizeDirEntry = sizeof(FAT_DIR_ENTRY); EntriesPerPage = FAT_ENTRIES_PER_PAGE; } ExAcquireResourceExclusiveLite (&DeviceExt->DirResource, TRUE); pFcb = vfatOpenRootFCB (DeviceExt); ExReleaseResourceLite (&DeviceExt->DirResource); FileOffset.QuadPart = 0; if (CcMapData(pFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, &Context, (PVOID*)&Entry)) { while (TRUE) { if (ENTRY_VOLUME(DeviceExt, Entry)) { /* copy volume label */ if (DeviceExt->Flags & VCB_IS_FATX) { StringO.Buffer = (PCHAR)Entry->FatX.Filename; StringO.MaximumLength = StringO.Length = Entry->FatX.FilenameLength; RtlOemStringToUnicodeString(&NameU, &StringO, FALSE); } else { vfat8Dot3ToString (&Entry->Fat, &NameU); } Vpb->VolumeLabelLength = NameU.Length; break; } if (ENTRY_END(DeviceExt, Entry)) { break; } DirIndex++; Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry); if ((DirIndex % EntriesPerPage) == 0) { CcUnpinData(Context); FileOffset.u.LowPart += PAGE_SIZE; if (!CcMapData(pFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, &Context, (PVOID*)&Entry)) { Context = NULL; break; } } } if (Context) { CcUnpinData(Context); } } ExAcquireResourceExclusiveLite (&DeviceExt->DirResource, TRUE); vfatReleaseFCB (DeviceExt, pFcb); ExReleaseResourceLite (&DeviceExt->DirResource); return STATUS_SUCCESS; } NTSTATUS FindFile ( PDEVICE_EXTENSION DeviceExt, PVFATFCB Parent, PUNICODE_STRING FileToFindU, PVFAT_DIRENTRY_CONTEXT DirContext, BOOLEAN First) /* * FUNCTION: Find a file */ { PWCHAR PathNameBuffer; USHORT PathNameBufferLength; NTSTATUS Status; PVOID Context = NULL; PVOID Page; PVFATFCB rcFcb; BOOLEAN Found; UNICODE_STRING PathNameU; UNICODE_STRING FileToFindUpcase; BOOLEAN WildCard; DPRINT ("FindFile(Parent %p, FileToFind '%wZ', DirIndex: %d)\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_VFAT); 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 ((rcFcb->Flags & FCB_IS_FATX_ENTRY) && !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 %d (%d)\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); ExFreePool(PathNameBuffer); return Status; } } /* FsRtlIsNameInExpression need the searched string to be upcase, * even if IgnoreCase is specified */ Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFindU, TRUE); if (!NT_SUCCESS(Status)) { ExFreePool(PathNameBuffer); return Status; } while(TRUE) { Status = DeviceExt->GetNextDirEntry(&Context, &Page, Parent, DirContext, First); First = FALSE; if (Status == STATUS_NO_MORE_ENTRIES) { break; } if (ENTRY_VOLUME(DeviceExt, &DirContext->DirEntry)) { 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("%d\n", DirContext->LongNameU.Length); DPRINT("FindFile: new Name %wZ, DirIndex %d\n", &DirContext->LongNameU, DirContext->DirIndex); if (Context) { CcUnpinData(Context); } RtlFreeUnicodeString(&FileToFindUpcase); ExFreePool(PathNameBuffer); return STATUS_SUCCESS; } DirContext->DirIndex++; } if (Context) { CcUnpinData(Context); } RtlFreeUnicodeString(&FileToFindUpcase); ExFreePool(PathNameBuffer); return Status; } static NTSTATUS VfatOpenFile ( PDEVICE_EXTENSION DeviceExt, PUNICODE_STRING PathNameU, PFILE_OBJECT FileObject, PVFATFCB* ParentFcb ) /* * FUNCTION: Opens a file */ { 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; (*ParentFcb)->RefCount++; } else { *ParentFcb = NULL; } if (!DeviceExt->FatInfo.FixedMedia) { Status = VfatBlockDeviceIoControl (DeviceExt->StorageDevice, IOCTL_DISK_CHECK_VERIFY, NULL, 0, NULL, 0, FALSE); if (Status == STATUS_VERIFY_REQUIRED) { PDEVICE_OBJECT DeviceToVerify; DPRINT ("Media change detected!\n"); DPRINT ("Device %p\n", DeviceExt->StorageDevice); /* Find the device to verify and reset the thread field to empty value again. */ DeviceToVerify = IoGetDeviceToVerify (PsGetCurrentThread ()); IoSetDeviceToVerify (PsGetCurrentThread (), NULL); Status = IoVerifyVolume (DeviceToVerify, FALSE); } if (!NT_SUCCESS(Status)) { DPRINT ("Status %lx\n", Status); *ParentFcb = NULL; return Status; } } if (*ParentFcb) { (*ParentFcb)->RefCount++; } /* 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; } if (Fcb->Flags & FCB_DELETE_PENDING) { vfatReleaseFCB (DeviceExt, Fcb); return STATUS_DELETE_PENDING; } DPRINT ("Attaching FCB to fileObject\n"); Status = vfatAttachFCBToFileObject (DeviceExt, Fcb, FileObject); if (!NT_SUCCESS(Status)) { vfatReleaseFCB (DeviceExt, Fcb); } return Status; } static NTSTATUS VfatCreateFile ( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /* * FUNCTION: Create or open a file */ { PIO_STACK_LOCATION Stack; PFILE_OBJECT FileObject; NTSTATUS Status = STATUS_SUCCESS; PDEVICE_EXTENSION DeviceExt; ULONG RequestedDisposition, RequestedOptions; PVFATCCB pCcb; PVFATFCB pFcb = NULL; PVFATFCB ParentFcb = NULL; PWCHAR c, last; BOOLEAN PagingFileCreate = FALSE; BOOLEAN Dots; UNICODE_STRING FileNameU; UNICODE_STRING PathNameU; /* Unpack the various parameters. */ Stack = IoGetCurrentIrpStackLocation (Irp); RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff); RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS; PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE; FileObject = Stack->FileObject; DeviceExt = DeviceObject->DeviceExtension; /* Check their validity. */ if (RequestedOptions & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) { return(STATUS_INVALID_PARAMETER); } if (RequestedOptions & FILE_DIRECTORY_FILE && RequestedOptions & FILE_NON_DIRECTORY_FILE) { return(STATUS_INVALID_PARAMETER); } /* This a open operation for the volume itself */ if (FileObject->FileName.Length == 0 && FileObject->RelatedFileObject == NULL) { if (RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) { return(STATUS_ACCESS_DENIED); } if (RequestedOptions & FILE_DIRECTORY_FILE) { return(STATUS_NOT_A_DIRECTORY); } pFcb = DeviceExt->VolumeFcb; pCcb = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList); if (pCcb == NULL) { return (STATUS_INSUFFICIENT_RESOURCES); } RtlZeroMemory(pCcb, sizeof(VFATCCB)); FileObject->SectionObjectPointer = &pFcb->SectionObjectPointers; FileObject->FsContext = pFcb; FileObject->FsContext2 = pCcb; pFcb->RefCount++; Irp->IoStatus.Information = FILE_OPENED; return(STATUS_SUCCESS); } /* * Check for illegal characters and illegale 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); } last = c - 1; Dots = TRUE; } else if (*c != L'.') { Dots = FALSE; } if (*c != '\\' && vfatIsLongIllegal(*c)) { return(STATUS_OBJECT_NAME_INVALID); } } if (FileObject->RelatedFileObject && PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[0] == L'\\') { return(STATUS_OBJECT_NAME_INVALID); } if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\') { PathNameU.Length -= sizeof(WCHAR); } /* Try opening the file. */ Status = VfatOpenFile (DeviceExt, &PathNameU, FileObject, &ParentFcb); /* * 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) { if (ParentFcb) { vfatReleaseFCB (DeviceExt, ParentFcb); } return(Status); } if (!NT_SUCCESS(Status) && ParentFcb == NULL) { DPRINT1("VfatOpenFile faild for '%wZ', status %x\n", &PathNameU, Status); return Status; } /* * 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) { ULONG Attributes; Attributes = Stack->Parameters.Create.FileAttributes; vfatSplitPathName(&PathNameU, NULL, &FileNameU); Status = VfatAddEntry (DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions, (UCHAR)(Attributes & FILE_ATTRIBUTE_VALID_FLAGS)); vfatReleaseFCB (DeviceExt, ParentFcb); if (NT_SUCCESS (Status)) { Status = vfatAttachFCBToFileObject (DeviceExt, pFcb, FileObject); if ( !NT_SUCCESS(Status) ) { vfatReleaseFCB (DeviceExt, pFcb); 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; } } else { return(Status); } } else { if (ParentFcb) { vfatReleaseFCB (DeviceExt, ParentFcb); } return(Status); } } else { if (ParentFcb) { vfatReleaseFCB (DeviceExt, ParentFcb); } /* Otherwise fail if the caller wanted to create a new file */ if (RequestedDisposition == FILE_CREATE) { Irp->IoStatus.Information = FILE_EXISTS; VfatCloseFile (DeviceExt, FileObject); return(STATUS_OBJECT_NAME_COLLISION); } pFcb = FileObject->FsContext; 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); return(Status); } } /* * Check the file has the requested attributes */ if (RequestedOptions & FILE_NON_DIRECTORY_FILE && *pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY) { VfatCloseFile (DeviceExt, FileObject); return(STATUS_FILE_IS_A_DIRECTORY); } if (RequestedOptions & FILE_DIRECTORY_FILE && !(*pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY)) { VfatCloseFile (DeviceExt, FileObject); return(STATUS_NOT_A_DIRECTORY); } #ifndef USE_ROS_CC_AND_FS if (!(*pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY)) { if (Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) { 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); return 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(!(pFcb->Flags & FCB_IS_PAGE_FILE)) { VfatCloseFile(DeviceExt, FileObject); return(STATUS_INVALID_PARAMETER); } } else { pFcb->Flags |= FCB_IS_PAGE_FILE; } } else { if (pFcb->Flags & FCB_IS_PAGE_FILE) { VfatCloseFile(DeviceExt, FileObject); return(STATUS_INVALID_PARAMETER); } } if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) { ExAcquireResourceExclusiveLite(&(pFcb->MainResource), TRUE); Status = VfatSetAllocationSizeInformation (FileObject, pFcb, DeviceExt, &Irp->Overlay.AllocationSize); ExReleaseResourceLite(&(pFcb->MainResource)); if (!NT_SUCCESS (Status)) { VfatCloseFile (DeviceExt, FileObject); 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 ); } pFcb->OpenHandleCount++; /* FIXME : test write access if requested */ return(Status); } NTSTATUS VfatCreate (PVFAT_IRP_CONTEXT IrpContext) /* * FUNCTION: Create or open a file */ { 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->Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest (IrpContext->Irp, IO_DISK_INCREMENT); VfatFreeIrpContext(IrpContext); return(STATUS_SUCCESS); } if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT)) { return(VfatQueueRequest (IrpContext)); } IrpContext->Irp->IoStatus.Information = 0; ExAcquireResourceExclusiveLite (&IrpContext->DeviceExt->DirResource, TRUE); Status = VfatCreateFile (IrpContext->DeviceObject, IrpContext->Irp); ExReleaseResourceLite (&IrpContext->DeviceExt->DirResource); IrpContext->Irp->IoStatus.Status = Status; IoCompleteRequest (IrpContext->Irp, (CCHAR)(NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT)); VfatFreeIrpContext(IrpContext); return(Status); } /* EOF */