/* * COPYRIGHT: See COPYRIGHT.TXT * PROJECT: Ext2 File System Driver for WinNT/2K/XP * FILE: fsctl.c * PROGRAMMER: Matt Wu * HOMEPAGE: http://www.ext2fsd.com * UPDATE HISTORY: */ /* INCLUDES *****************************************************************/ #include "ext2fs.h" /* GLOBALS ***************************************************************/ extern PEXT2_GLOBAL Ext2Global; /* DEFINITIONS *************************************************************/ #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, Ext2IsHandleCountZero) #pragma alloc_text(PAGE, Ext2LockVcb) #pragma alloc_text(PAGE, Ext2LockVolume) #pragma alloc_text(PAGE, Ext2UnlockVcb) #pragma alloc_text(PAGE, Ext2UnlockVolume) #pragma alloc_text(PAGE, Ext2AllowExtendedDasdIo) #pragma alloc_text(PAGE, Ext2GetRetrievalPointerBase) #pragma alloc_text(PAGE, Ext2QueryExtentMappings) #pragma alloc_text(PAGE, Ext2QueryRetrievalPointers) #pragma alloc_text(PAGE, Ext2GetRetrievalPointers) #pragma alloc_text(PAGE, Ext2UserFsRequest) #pragma alloc_text(PAGE, Ext2IsMediaWriteProtected) #pragma alloc_text(PAGE, Ext2MountVolume) #pragma alloc_text(PAGE, Ext2PurgeVolume) #pragma alloc_text(PAGE, Ext2PurgeFile) #pragma alloc_text(PAGE, Ext2DismountVolume) #pragma alloc_text(PAGE, Ext2IsVolumeMounted) #pragma alloc_text(PAGE, Ext2VerifyVolume) #pragma alloc_text(PAGE, Ext2FileSystemControl) #endif VOID Ext2SetVpbFlag ( IN PVPB Vpb, IN USHORT Flag ) { KIRQL OldIrql; IoAcquireVpbSpinLock(&OldIrql); Vpb->Flags |= Flag; IoReleaseVpbSpinLock(OldIrql); } VOID Ext2ClearVpbFlag ( IN PVPB Vpb, IN USHORT Flag ) { KIRQL OldIrql; IoAcquireVpbSpinLock(&OldIrql); Vpb->Flags &= ~Flag; IoReleaseVpbSpinLock(OldIrql); } BOOLEAN Ext2IsHandleCountZero(IN PEXT2_VCB Vcb) { PEXT2_FCB Fcb; PLIST_ENTRY List; for ( List = Vcb->FcbList.Flink; List != &Vcb->FcbList; List = List->Flink ) { Fcb = CONTAINING_RECORD(List, EXT2_FCB, Next); ASSERT((Fcb->Identifier.Type == EXT2FCB) && (Fcb->Identifier.Size == sizeof(EXT2_FCB))); DEBUG(DL_INF, ( "Ext2IsHandleCountZero: Inode:%xh File:%S OpenHandleCount=%xh\n", Fcb->Inode->i_ino, Fcb->Mcb->ShortName.Buffer, Fcb->OpenHandleCount)); if (Fcb->OpenHandleCount) { return FALSE; } } return TRUE; } NTSTATUS Ext2LockVcb (IN PEXT2_VCB Vcb, IN PFILE_OBJECT FileObject) { NTSTATUS Status = STATUS_SUCCESS; _SEH2_TRY { if (FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) { DEBUG(DL_INF, ( "Ext2LockVolume: Volume is already locked.\n")); Status = STATUS_ACCESS_DENIED; _SEH2_LEAVE; } if (Vcb->OpenHandleCount > (ULONG)(FileObject ? 1 : 0)) { DEBUG(DL_INF, ( "Ext2LockVcb: There are still opened files.\n")); Status = STATUS_ACCESS_DENIED; _SEH2_LEAVE; } if (!Ext2IsHandleCountZero(Vcb)) { DEBUG(DL_INF, ( "Ext2LockVcb: Thare are still opened files.\n")); Status = STATUS_ACCESS_DENIED; _SEH2_LEAVE; } SetLongFlag(Vcb->Flags, VCB_VOLUME_LOCKED); Ext2SetVpbFlag(Vcb->Vpb, VPB_LOCKED); Vcb->LockFile = FileObject; DEBUG(DL_INF, ( "Ext2LockVcb: Volume locked.\n")); } _SEH2_FINALLY { // Nothing } _SEH2_END; return Status; } NTSTATUS Ext2LockVolume (IN PEXT2_IRP_CONTEXT IrpContext) { PIO_STACK_LOCATION IrpSp; PDEVICE_OBJECT DeviceObject; PEXT2_VCB Vcb = NULL; NTSTATUS Status; BOOLEAN VcbResourceAcquired = FALSE; _SEH2_TRY { ASSERT(IrpContext != NULL); ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; Status = STATUS_UNSUCCESSFUL; // // This request is not allowed on the main device object // if (IsExt2FsDevice(DeviceObject)) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); ASSERT(IsMounted(Vcb)); IrpSp = IoGetCurrentIrpStackLocation(IrpContext->Irp); #if (_WIN32_WINNT >= 0x0500) CcWaitForCurrentLazyWriterActivity(); #endif ExAcquireResourceExclusiveLite( &Vcb->MainResource, TRUE ); VcbResourceAcquired = TRUE; /* flush dirty data before locking the volume */ if (!IsVcbReadOnly(Vcb)) { Ext2FlushFiles(IrpContext, Vcb, FALSE); Ext2FlushVolume(IrpContext, Vcb, FALSE); } Status = Ext2LockVcb(Vcb, IrpSp->FileObject); } _SEH2_FINALLY { if (VcbResourceAcquired) { ExReleaseResourceLite(&Vcb->MainResource); } if (!IrpContext->ExceptionInProgress) { Ext2CompleteIrpContext(IrpContext, Status); } } _SEH2_END; return Status; } NTSTATUS Ext2UnlockVcb ( IN PEXT2_VCB Vcb, IN PFILE_OBJECT FileObject ) { NTSTATUS Status; _SEH2_TRY { if (FileObject && FileObject->FsContext != Vcb) { Status = STATUS_NOT_LOCKED; _SEH2_LEAVE; } if (!FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) { DEBUG(DL_ERR, ( ": Ext2UnlockVcb: Volume is not locked.\n")); Status = STATUS_NOT_LOCKED; _SEH2_LEAVE; } if (Vcb->LockFile == FileObject) { ClearFlag(Vcb->Flags, VCB_VOLUME_LOCKED); Ext2ClearVpbFlag(Vcb->Vpb, VPB_LOCKED); DEBUG(DL_INF, ( "Ext2UnlockVcb: Volume unlocked.\n")); Status = STATUS_SUCCESS; } else { Status = STATUS_NOT_LOCKED; } } _SEH2_FINALLY { // Nothing } _SEH2_END; return Status; } NTSTATUS Ext2UnlockVolume ( IN PEXT2_IRP_CONTEXT IrpContext ) { PIO_STACK_LOCATION IrpSp = NULL; PDEVICE_OBJECT DeviceObject = NULL; PEXT2_VCB Vcb = NULL; NTSTATUS Status; BOOLEAN VcbResourceAcquired = FALSE; _SEH2_TRY { ASSERT(IrpContext != NULL); ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; IrpSp = IoGetCurrentIrpStackLocation(IrpContext->Irp); // // This request is not allowed on the main device object // if (IsExt2FsDevice(DeviceObject)) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); ExAcquireResourceExclusiveLite( &Vcb->MainResource, TRUE ); VcbResourceAcquired = TRUE; Status = Ext2UnlockVcb(Vcb, IrpSp->FileObject); } _SEH2_FINALLY { if (VcbResourceAcquired) { ExReleaseResourceLite(&Vcb->MainResource); } if (!IrpContext->ExceptionInProgress) { Ext2CompleteIrpContext(IrpContext, Status); } } _SEH2_END; return Status; } NTSTATUS Ext2InvalidateVolumes ( IN PEXT2_IRP_CONTEXT IrpContext ) { NTSTATUS Status; PIRP Irp; PIO_STACK_LOCATION IrpSp; #ifndef __REACTOS__ PVPB NewVpb = NULL; #endif HANDLE Handle; PLIST_ENTRY ListEntry; ULONG InputLength = 0; PFILE_OBJECT FileObject; PDEVICE_OBJECT DeviceObject; BOOLEAN GlobalResourceAcquired = FALSE; LUID Privilege = {SE_TCB_PRIVILEGE, 0}; _SEH2_TRY { Irp = IrpContext->Irp; IrpSp = IoGetCurrentIrpStackLocation(Irp); if (!IsExt2FsDevice(IrpSp->DeviceObject)) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } if (!SeSinglePrivilegeCheck(Privilege, Irp->RequestorMode)) { Status = STATUS_PRIVILEGE_NOT_HELD; _SEH2_LEAVE; } #ifndef _GNU_NTIFS_ InputLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; #else InputLength = ((PEXTENDED_IO_STACK_LOCATION)(IrpSp))-> Parameters.FileSystemControl.InputBufferLength; #endif #if defined(_WIN64) if (IoIs32bitProcess(Irp)) { if (InputLength != sizeof(UINT32)) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } Handle = (HANDLE) LongToHandle( (*(PUINT32)Irp->AssociatedIrp.SystemBuffer) ); } else #endif { if (InputLength != sizeof(HANDLE)) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } Handle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; } Status = ObReferenceObjectByHandle( Handle, 0, *IoFileObjectType, KernelMode, (void **)&FileObject, NULL ); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } else { DeviceObject = FileObject->DeviceObject; ObDereferenceObject(FileObject); } ExAcquireResourceExclusiveLite(&Ext2Global->Resource, TRUE); GlobalResourceAcquired = TRUE; ListEntry = Ext2Global->VcbList.Flink; while (ListEntry != &Ext2Global->VcbList) { PEXT2_VCB Vcb = CONTAINING_RECORD(ListEntry, EXT2_VCB, Next); ListEntry = ListEntry->Flink; DEBUG(DL_DBG, ( "Ext2InvalidateVolumes: Vcb=%xh Vcb->Vpb=%xh " "Blink = %p &Vcb->Next = %p\n", Vcb, Vcb->Vpb, ListEntry->Blink, &Vcb->Next)); if (Vcb->Vpb && (Vcb->Vpb->RealDevice == DeviceObject)) { DEBUG(DL_DBG, ( "Ext2InvalidateVolumes: Got Vcb=%xh Vcb->Vpb=%xh " "Blink = %p &Vcb->Next = %p\n", Vcb, Vcb->Vpb, ListEntry->Blink, &Vcb->Next)); /* dismount the volume */ Ext2CheckDismount(IrpContext, Vcb, FALSE); } } } _SEH2_FINALLY { if (GlobalResourceAcquired) { ExReleaseResourceLite(&Ext2Global->Resource); } if (!IrpContext->ExceptionInProgress) { Ext2CompleteIrpContext(IrpContext, Status); } } _SEH2_END; return Status; } NTSTATUS Ext2AllowExtendedDasdIo(IN PEXT2_IRP_CONTEXT IrpContext) { PIO_STACK_LOCATION IrpSp; PEXT2_VCB Vcb; PEXT2_CCB Ccb; NTSTATUS status; IrpSp = IoGetCurrentIrpStackLocation(IrpContext->Irp); Vcb = (PEXT2_VCB) IrpSp->FileObject->FsContext; Ccb = (PEXT2_CCB) IrpSp->FileObject->FsContext2; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); ASSERT(IsMounted(Vcb)); if (Ccb) { SetLongFlag(Ccb->Flags, CCB_ALLOW_EXTENDED_DASD_IO); status = STATUS_SUCCESS; } else { status = STATUS_INVALID_PARAMETER; } Ext2CompleteIrpContext(IrpContext, status); return status; } /* * Ext2OplockRequest * * oplock requests handler routine * * Arguments: * IrpContext: the ext2 irp context * * Return Value: * NTSTATUS: The return status for the operation * */ NTSTATUS Ext2OplockRequest ( IN PEXT2_IRP_CONTEXT IrpContext ) { NTSTATUS Status; ULONG FsCtrlCode; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; PIRP Irp = NULL; PIO_STACK_LOCATION IrpSp; PEXTENDED_IO_STACK_LOCATION EIrpSp; PEXT2_VCB Vcb = NULL; PEXT2_FCB Fcb = NULL; PEXT2_CCB Ccb = NULL; ULONG OplockCount = 0; BOOLEAN VcbResourceAcquired = FALSE; BOOLEAN FcbResourceAcquired = FALSE; ASSERT(IrpContext); _SEH2_TRY { Irp = IrpContext->Irp; ASSERT(Irp); IrpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(IrpSp); EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; // // This request is not allowed on the main device object // if (IsExt2FsDevice(DeviceObject)) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); ASSERT(IsMounted(Vcb)); FileObject = IrpContext->FileObject; Fcb = (PEXT2_FCB) FileObject->FsContext; // // This request is not allowed on volumes // if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Fcb->Identifier.Type == EXT2FCB) && (Fcb->Identifier.Size == sizeof(EXT2_FCB))); if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { Status = STATUS_FILE_DELETED; _SEH2_LEAVE; } Ccb = (PEXT2_CCB) FileObject->FsContext2; if (Ccb == NULL) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Ccb->Identifier.Type == EXT2CCB) && (Ccb->Identifier.Size == sizeof(EXT2_CCB))); FsCtrlCode = EIrpSp->Parameters.FileSystemControl.FsControlCode; switch (FsCtrlCode) { case FSCTL_REQUEST_OPLOCK_LEVEL_1: case FSCTL_REQUEST_OPLOCK_LEVEL_2: case FSCTL_REQUEST_BATCH_OPLOCK: VcbResourceAcquired = ExAcquireResourceSharedLite( &Vcb->MainResource, IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); FcbResourceAcquired = ExAcquireResourceExclusiveLite ( &Fcb->MainResource, IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); if (FsCtrlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) { OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks(&Fcb->FileLockAnchor); } else { OplockCount = Fcb->OpenHandleCount; } break; case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: case FSCTL_OPBATCH_ACK_CLOSE_PENDING : case FSCTL_OPLOCK_BREAK_NOTIFY: case FSCTL_OPLOCK_BREAK_ACK_NO_2: FcbResourceAcquired = ExAcquireResourceSharedLite ( &Fcb->MainResource, IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); break; default: Ext2BugCheck(EXT2_BUGCHK_FSCTL, FsCtrlCode, 0, 0); } // // Call the FsRtl routine to grant/acknowledge oplock. // Status = FsRtlOplockFsctrl( &Fcb->Oplock, Irp, OplockCount ); // // Set the flag indicating if Fast I/O is possible // Fcb->Header.IsFastIoPossible = Ext2IsFastIoPossible(Fcb); IrpContext->Irp = NULL; } _SEH2_FINALLY { if (FcbResourceAcquired) { ExReleaseResourceLite(&Fcb->MainResource); } if (VcbResourceAcquired) { ExReleaseResourceLite(&Vcb->MainResource); } if (!_SEH2_AbnormalTermination()) { Ext2CompleteIrpContext(IrpContext, Status); } } _SEH2_END; return Status; } NTSTATUS Ext2IsVolumeDirty ( IN PEXT2_IRP_CONTEXT IrpContext ) { NTSTATUS status = STATUS_SUCCESS; PIRP Irp; PEXTENDED_IO_STACK_LOCATION IrpSp; PULONG VolumeState; _SEH2_TRY { Irp = IrpContext->Irp; IrpSp = (PEXTENDED_IO_STACK_LOCATION)IoGetCurrentIrpStackLocation(Irp); // // Get a pointer to the output buffer. Look at the system buffer field in th // irp first. Then the Irp Mdl. // if (Irp->AssociatedIrp.SystemBuffer != NULL) { VolumeState = Irp->AssociatedIrp.SystemBuffer; } else if (Irp->MdlAddress != NULL) { VolumeState = MmGetSystemAddressForMdl( Irp->MdlAddress ); } else { status = STATUS_INVALID_USER_BUFFER; _SEH2_LEAVE; } if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) { status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } *VolumeState = 0; } _SEH2_FINALLY { if (!IrpContext->ExceptionInProgress) { Ext2CompleteIrpContext(IrpContext, status); } } _SEH2_END; return status; } NTSTATUS Ext2QueryExtentMappings( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, IN PEXT2_FCB Fcb, IN PLARGE_INTEGER RequestVbn, OUT PLARGE_INTEGER * pMappedRuns ) { PLARGE_INTEGER MappedRuns = NULL; PLARGE_INTEGER PartialRuns = NULL; PEXT2_EXTENT Chain = NULL; PEXT2_EXTENT Extent = NULL; LONGLONG Vbn = 0; ULONG Length = 0; ULONG i = 0; NTSTATUS Status = STATUS_SUCCESS; _SEH2_TRY { /* now building all the request extents */ while (Vbn < RequestVbn->QuadPart) { Length = 0x80000000; /* 2g bytes */ if (RequestVbn->QuadPart < Vbn + Length) { Length = (ULONG)(RequestVbn->QuadPart - Vbn); } /* build extents for sub-range */ Extent = NULL; Status = Ext2BuildExtents( IrpContext, Vcb, Fcb->Mcb, Vbn, Length, FALSE, &Extent); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } if (Chain) { Ext2JointExtents(Chain, Extent); } else { Chain = Extent; } /* allocate extent array */ PartialRuns = Ext2AllocatePool( NonPagedPool, (Ext2CountExtents(Chain) + 2) * (2 * sizeof(LARGE_INTEGER)), 'RE2E'); if (PartialRuns == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } RtlZeroMemory( PartialRuns, (Ext2CountExtents(Chain) + 2) * (2 * sizeof(LARGE_INTEGER))); if (MappedRuns) { RtlMoveMemory(PartialRuns, MappedRuns, i * 2 * sizeof(LARGE_INTEGER)); Ext2FreePool(MappedRuns, 'RE2E'); } MappedRuns = PartialRuns; /* walk all the Mcb runs in Extent */ for (; Extent != NULL; Extent = Extent->Next) { MappedRuns[i*2 + 0].QuadPart = Vbn + Extent->Offset; MappedRuns[i*2 + 1].QuadPart = Extent->Lba; i = i+1; } Vbn = Vbn + Length; } *pMappedRuns = MappedRuns; } _SEH2_FINALLY { if (!NT_SUCCESS(Status) || Status == STATUS_PENDING) { if (MappedRuns) { Ext2FreePool(MappedRuns, 'RE2E'); } *pMappedRuns = NULL; } if (Chain) { Ext2DestroyExtentChain(Chain); } } _SEH2_END; return Status; } NTSTATUS Ext2QueryRetrievalPointers ( IN PEXT2_IRP_CONTEXT IrpContext ) { PIRP Irp = NULL; PIO_STACK_LOCATION IrpSp; PEXTENDED_IO_STACK_LOCATION EIrpSp; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; PEXT2_VCB Vcb = NULL; PEXT2_FCB Fcb = NULL; PEXT2_CCB Ccb = NULL; PLARGE_INTEGER RequestVbn; PLARGE_INTEGER * pMappedRuns; ULONG InputSize; ULONG OutputSize; NTSTATUS Status = STATUS_SUCCESS; BOOLEAN FcbResourceAcquired = FALSE; _SEH2_TRY { ASSERT(IrpContext); Irp = IrpContext->Irp; ASSERT(Irp); IrpSp = IoGetCurrentIrpStackLocation(Irp); EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; ASSERT(IrpSp); InputSize = EIrpSp->Parameters.FileSystemControl.InputBufferLength; OutputSize = EIrpSp->Parameters.FileSystemControl.OutputBufferLength; ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; DbgBreak(); /* This request is not allowed on the main device object */ if (IsExt2FsDevice(DeviceObject)) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); ASSERT(IsMounted(Vcb)); FileObject = IrpContext->FileObject; Fcb = (PEXT2_FCB) FileObject->FsContext; /* check Fcb is valid or not */ if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Fcb->Identifier.Type == EXT2FCB) && (Fcb->Identifier.Size == sizeof(EXT2_FCB))); if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { Status = STATUS_FILE_DELETED; _SEH2_LEAVE; } Ccb = (PEXT2_CCB) FileObject->FsContext2; if (Ccb == NULL) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Ccb->Identifier.Type == EXT2CCB) && (Ccb->Identifier.Size == sizeof(EXT2_CCB))); /* Is requstor in kernel and Fcb a paging file ? */ if (Irp->RequestorMode != KernelMode || !IsFlagOn(Fcb->Flags, FCB_PAGE_FILE) || InputSize != sizeof(LARGE_INTEGER) || OutputSize != sizeof(PVOID)) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } if (!ExAcquireResourceExclusiveLite ( &Fcb->MainResource, Ext2CanIWait())) { Status = STATUS_PENDING; _SEH2_LEAVE; } FcbResourceAcquired = TRUE; RequestVbn = EIrpSp->Parameters.FileSystemControl.Type3InputBuffer; pMappedRuns = Irp->UserBuffer; DbgBreak(); /* request size beyonds whole file size */ if (RequestVbn->QuadPart >= Fcb->Header.AllocationSize.QuadPart) { Status = STATUS_END_OF_FILE; _SEH2_LEAVE; } Status = Ext2QueryExtentMappings( IrpContext, Vcb, Fcb, RequestVbn, pMappedRuns ); } _SEH2_FINALLY { if (FcbResourceAcquired) { ExReleaseResourceLite(&Fcb->MainResource); } if (!_SEH2_AbnormalTermination()) { if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { Status = Ext2QueueRequest(IrpContext); } else { Ext2CompleteIrpContext(IrpContext, Status); } } } _SEH2_END; return Status; } NTSTATUS Ext2GetRetrievalPointers ( IN PEXT2_IRP_CONTEXT IrpContext ) { PIRP Irp = NULL; PIO_STACK_LOCATION IrpSp; PEXTENDED_IO_STACK_LOCATION EIrpSp; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; PEXT2_VCB Vcb = NULL; PEXT2_FCB Fcb = NULL; PEXT2_CCB Ccb = NULL; PSTARTING_VCN_INPUT_BUFFER SVIB; PRETRIEVAL_POINTERS_BUFFER RPSB; PEXT2_EXTENT Chain = NULL; PEXT2_EXTENT Extent = NULL; LONGLONG Vbn = 0; ULONG Length = 0; ULONG i = 0; ULONG UsedSize = 0; ULONG InputSize; ULONG OutputSize; NTSTATUS Status = STATUS_SUCCESS; BOOLEAN FcbResourceAcquired = FALSE; _SEH2_TRY { ASSERT(IrpContext); Irp = IrpContext->Irp; ASSERT(Irp); IrpSp = IoGetCurrentIrpStackLocation(Irp); EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; ASSERT(IrpSp); InputSize = EIrpSp->Parameters.FileSystemControl.InputBufferLength; OutputSize = EIrpSp->Parameters.FileSystemControl.OutputBufferLength; ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; /* This request is not allowed on the main device object */ if (IsExt2FsDevice(DeviceObject)) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); ASSERT(IsMounted(Vcb)); FileObject = IrpContext->FileObject; Fcb = (PEXT2_FCB) FileObject->FsContext; /* check Fcb is valid or not */ if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Fcb->Identifier.Type == EXT2FCB) && (Fcb->Identifier.Size == sizeof(EXT2_FCB))); if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { Status = STATUS_FILE_DELETED; _SEH2_LEAVE; } Ccb = (PEXT2_CCB) FileObject->FsContext2; if (Ccb == NULL) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Ccb->Identifier.Type == EXT2CCB) && (Ccb->Identifier.Size == sizeof(EXT2_CCB))); if (InputSize < sizeof(STARTING_VCN_INPUT_BUFFER) || OutputSize < sizeof(RETRIEVAL_POINTERS_BUFFER) ) { Status = STATUS_BUFFER_TOO_SMALL; _SEH2_LEAVE; } if (!ExAcquireResourceExclusiveLite ( &Fcb->MainResource, Ext2CanIWait())) { Status = STATUS_PENDING; _SEH2_LEAVE; } FcbResourceAcquired = TRUE; SVIB = (PSTARTING_VCN_INPUT_BUFFER) EIrpSp->Parameters.FileSystemControl.Type3InputBuffer; RPSB = (PRETRIEVAL_POINTERS_BUFFER) Ext2GetUserBuffer(Irp); /* probe user buffer */ _SEH2_TRY { if (Irp->RequestorMode != KernelMode) { ProbeForRead (SVIB, InputSize, sizeof(UCHAR)); ProbeForWrite(RPSB, OutputSize, sizeof(UCHAR)); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_INVALID_USER_BUFFER; } _SEH2_END; if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } UsedSize = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[0]); /* request size beyonds whole file size ? */ DEBUG(DL_DBG, ("Ext2GetRetrievalPointers: Startin from Vbn: %I64xh\n", SVIB->StartingVcn.QuadPart)); Vbn = (SVIB->StartingVcn.QuadPart << BLOCK_BITS); if (Vbn >= Fcb->Header.AllocationSize.QuadPart ) { Status = STATUS_END_OF_FILE; _SEH2_LEAVE; } /* now building all the request extents */ while (Vbn < Fcb->Header.AllocationSize.QuadPart) { ASSERT(Chain == NULL); Length = 0x80000000; /* 2g bytes */ if (Fcb->Header.AllocationSize.QuadPart < Vbn + Length) { Length = (ULONG)(Fcb->Header.AllocationSize.QuadPart - Vbn); } /* build extents for sub-range */ Status = Ext2BuildExtents( IrpContext, Vcb, Fcb->Mcb, Vbn, Length, FALSE, &Chain); if (!NT_SUCCESS(Status)) { DbgBreak(); _SEH2_LEAVE; } /* fill user buffer of RETRIEVAL_POINTERS_BUFFER */ Extent = Chain; while (Extent) { DEBUG(DL_MAP, ("Ext2GetRetrievalPointers: %wZ %d Vbn = %I64xh Lbn = %I64xh\n", &Fcb->Mcb->FullName, i, ((Vbn + Extent->Offset) >> BLOCK_BITS), Extent->Lba)); RPSB->Extents[i].Lcn.QuadPart = (Extent->Lba >> BLOCK_BITS); RPSB->Extents[i].NextVcn.QuadPart = ((Vbn + Extent->Offset + Extent->Length) >> BLOCK_BITS); if (i == 0) { RPSB->StartingVcn.QuadPart = ((Vbn + Extent->Offset) >> BLOCK_BITS); } else { ASSERT(RPSB->Extents[i-1].NextVcn.QuadPart == ((Vbn + Extent->Offset) >> BLOCK_BITS)); } if (UsedSize + sizeof(RETRIEVAL_POINTERS_BUFFER) > OutputSize) { Status = STATUS_BUFFER_OVERFLOW; _SEH2_LEAVE; } UsedSize += sizeof(LARGE_INTEGER) * 2; Irp->IoStatus.Information = (ULONG_PTR)UsedSize; RPSB->ExtentCount = ++i; Extent = Extent->Next; } if (Chain) { Ext2DestroyExtentChain(Chain); Chain = NULL; } Vbn = Vbn + Length; } #if 0 { NTSTATUS _s; ULONG _i = 0; LARGE_INTEGER RequestVbn = Fcb->Header.AllocationSize; PLARGE_INTEGER MappedRuns = NULL; _s = Ext2QueryExtentMappings( IrpContext, Vcb, Fcb, &RequestVbn, &MappedRuns ); if (!NT_SUCCESS(_s) || NULL == MappedRuns) { DbgBreak(); goto exit_to_get_rps; } while (MappedRuns[_i*2 + 0].QuadPart != 0 || MappedRuns[_i*2 + 1].QuadPart != 0 ) { DEBUG(DL_MAP, ("Ext2QueryExtentMappings: %wZ %d Vbn = %I64xh Lbn = %I64xh\n", &Fcb->Mcb->FullName, _i, MappedRuns[_i*2 + 0].QuadPart, MappedRuns[_i*2 + 1].QuadPart)); _i++; } exit_to_get_rps: if (MappedRuns) { Ext2FreePool(MappedRuns, 'RE2E'); } } #endif } _SEH2_FINALLY { if (FcbResourceAcquired) { ExReleaseResourceLite(&Fcb->MainResource); } if (Chain) { Ext2DestroyExtentChain(Chain); } if (!_SEH2_AbnormalTermination()) { if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { Status = Ext2QueueRequest(IrpContext); } else { Ext2CompleteIrpContext(IrpContext, Status); } } } _SEH2_END; return Status; } NTSTATUS Ext2GetRetrievalPointerBase ( IN PEXT2_IRP_CONTEXT IrpContext ) { PIRP Irp = NULL; PIO_STACK_LOCATION IrpSp; PEXTENDED_IO_STACK_LOCATION EIrpSp; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; PEXT2_VCB Vcb = NULL; PEXT2_FCB Fcb = NULL; PEXT2_CCB Ccb = NULL; PLARGE_INTEGER FileAreaOffset; ULONG OutputSize; NTSTATUS Status = STATUS_SUCCESS; BOOLEAN FcbResourceAcquired = FALSE; _SEH2_TRY { ASSERT(IrpContext); Irp = IrpContext->Irp; ASSERT(Irp); IrpSp = IoGetCurrentIrpStackLocation(Irp); EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; ASSERT(IrpSp); OutputSize = EIrpSp->Parameters.FileSystemControl.OutputBufferLength; ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; /* This request is not allowed on the main device object */ if (IsExt2FsDevice(DeviceObject)) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); ASSERT(IsMounted(Vcb)); FileObject = IrpContext->FileObject; Fcb = (PEXT2_FCB) FileObject->FsContext; /* check Fcb is valid or not */ if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Fcb->Identifier.Type == EXT2FCB) && (Fcb->Identifier.Size == sizeof(EXT2_FCB))); if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { Status = STATUS_FILE_DELETED; _SEH2_LEAVE; } Ccb = (PEXT2_CCB) FileObject->FsContext2; if (Ccb == NULL) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Ccb->Identifier.Type == EXT2CCB) && (Ccb->Identifier.Size == sizeof(EXT2_CCB))); if (OutputSize < sizeof(LARGE_INTEGER)) { Status = STATUS_BUFFER_TOO_SMALL; _SEH2_LEAVE; } if (!ExAcquireResourceExclusiveLite ( &Fcb->MainResource, Ext2CanIWait())) { Status = STATUS_PENDING; _SEH2_LEAVE; } FcbResourceAcquired = TRUE; FileAreaOffset = (PLARGE_INTEGER) Ext2GetUserBuffer(Irp); /* probe user buffer */ _SEH2_TRY { if (Irp->RequestorMode != KernelMode) { ProbeForWrite(FileAreaOffset, OutputSize, sizeof(UCHAR)); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_INVALID_USER_BUFFER; } _SEH2_END; if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } DEBUG(DL_DBG, ("Ext2GetRetrievalPointerBase: FileAreaOffset is 0.\n")); FileAreaOffset->QuadPart = 0; // sector offset to the first allocatable unit on the filesystem Irp->IoStatus.Information = sizeof(LARGE_INTEGER); } _SEH2_FINALLY { if (FcbResourceAcquired) { ExReleaseResourceLite(&Fcb->MainResource); } if (!_SEH2_AbnormalTermination()) { if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { Status = Ext2QueueRequest(IrpContext); } else { Ext2CompleteIrpContext(IrpContext, Status); } } } _SEH2_END; return Status; } NTSTATUS Ext2InspectReparseData( IN PREPARSE_DATA_BUFFER RDB, IN ULONG InputBufferLength ) { NTSTATUS Status = STATUS_SUCCESS; if (!RDB) { Status = STATUS_INVALID_PARAMETER; goto out; } if (InputBufferLength < sizeof(REPARSE_DATA_BUFFER)) { Status = STATUS_BUFFER_OVERFLOW; goto out; } if (InputBufferLength < RDB->ReparseDataLength) { Status = STATUS_BUFFER_OVERFLOW; goto out; } if (RDB->ReparseTag != IO_REPARSE_TAG_SYMLINK) { Status = STATUS_NOT_IMPLEMENTED; goto out; } if ((PUCHAR)RDB->SymbolicLinkReparseBuffer.PathBuffer + RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset + RDB->SymbolicLinkReparseBuffer.SubstituteNameLength > (PUCHAR)RDB + InputBufferLength ) { Status = STATUS_BUFFER_OVERFLOW; goto out; } if ((PUCHAR)RDB->SymbolicLinkReparseBuffer.PathBuffer + RDB->SymbolicLinkReparseBuffer.PrintNameOffset + RDB->SymbolicLinkReparseBuffer.PrintNameLength > (PUCHAR)RDB + InputBufferLength) { Status = STATUS_BUFFER_OVERFLOW; goto out; } if (RDB->SymbolicLinkReparseBuffer.Flags != SYMLINK_FLAG_RELATIVE) { Status = STATUS_NOT_IMPLEMENTED; goto out; } out: return Status; } VOID Ext2InitializeReparseData(IN PREPARSE_DATA_BUFFER RDB, USHORT PathBufferLength) { ASSERT(FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset) == REPARSE_DATA_BUFFER_HEADER_SIZE); RDB->ReparseTag = IO_REPARSE_TAG_SYMLINK; RDB->ReparseDataLength = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + PathBufferLength * sizeof(WCHAR); RDB->Reserved = 0; RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset = PathBufferLength; RDB->SymbolicLinkReparseBuffer.SubstituteNameLength = PathBufferLength; RDB->SymbolicLinkReparseBuffer.PrintNameOffset = 0; RDB->SymbolicLinkReparseBuffer.PrintNameLength = PathBufferLength; RDB->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE; RtlZeroMemory(&RDB->SymbolicLinkReparseBuffer.PathBuffer, PathBufferLength * 2); } NTSTATUS Ext2ReadSymlink ( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, IN PEXT2_MCB Mcb, IN PVOID Buffer, IN ULONG Size, OUT PULONG BytesRead ) { return Ext2ReadInode ( IrpContext, Vcb, Mcb, 0, Buffer, Size, FALSE, BytesRead); } NTSTATUS Ext2GetReparsePoint (IN PEXT2_IRP_CONTEXT IrpContext) { PIRP Irp = NULL; PIO_STACK_LOCATION IrpSp; PEXTENDED_IO_STACK_LOCATION EIrpSp; PDEVICE_OBJECT DeviceObject; PEXT2_VCB Vcb = NULL; PEXT2_CCB Ccb = NULL; PEXT2_MCB Mcb = NULL; NTSTATUS Status = STATUS_UNSUCCESSFUL; BOOLEAN MainResourceAcquired = FALSE; PVOID OutputBuffer; ULONG OutputBufferLength; ULONG BytesRead = 0; PREPARSE_DATA_BUFFER RDB; UNICODE_STRING UniName; OEM_STRING OemName; PCHAR OemNameBuffer = NULL; int OemNameLength = 0, i; Ccb = IrpContext->Ccb; ASSERT(Ccb != NULL); ASSERT((Ccb->Identifier.Type == EXT2CCB) && (Ccb->Identifier.Size == sizeof(EXT2_CCB))); DeviceObject = IrpContext->DeviceObject; Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; Mcb = IrpContext->Fcb->Mcb; Irp = IrpContext->Irp; IrpSp = IoGetCurrentIrpStackLocation(Irp); _SEH2_TRY { if (!Mcb || !IsInodeSymLink(&Mcb->Inode) || !IsFlagOn(Ccb->Flags, CCB_OPEN_REPARSE_POINT)) { Status = STATUS_NOT_A_REPARSE_POINT; _SEH2_LEAVE; } OutputBuffer = (PVOID)Irp->AssociatedIrp.SystemBuffer; OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; RDB = (PREPARSE_DATA_BUFFER)OutputBuffer; if (!RDB) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } if (OutputBufferLength < sizeof(REPARSE_DATA_BUFFER)) { Status = STATUS_BUFFER_OVERFLOW; _SEH2_LEAVE; } OemNameLength = (ULONG)Mcb->Inode.i_size; if (OemNameLength > USHRT_MAX) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } OemName.Length = (USHORT)OemNameLength; OemName.MaximumLength = OemNameLength + 1; OemNameBuffer = OemName.Buffer = Ext2AllocatePool(NonPagedPool, OemName.MaximumLength, 'NL2E'); if (!OemNameBuffer) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } Status = Ext2ReadSymlink(IrpContext, Vcb, Mcb, OemNameBuffer, OemNameLength, &BytesRead ); OemName.Buffer[OemName.Length] = '\0'; for (i = 0;i < OemName.Length;i++) { if (OemName.Buffer[i] == '/') { OemName.Buffer[i] = '\\'; } } if (OutputBufferLength - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) > USHRT_MAX) { UniName.Length = USHRT_MAX; } else { UniName.Length = (USHORT)OutputBufferLength - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer); } UniName.MaximumLength = UniName.Length; UniName.Length = (USHORT)Ext2OEMToUnicodeSize(Vcb, &OemName); Irp->IoStatus.Information = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + 2 * UniName.Length; if (UniName.MaximumLength < 2*UniName.Length) { Status = STATUS_BUFFER_TOO_SMALL; _SEH2_LEAVE; } Ext2InitializeReparseData(RDB, UniName.Length); UniName.Buffer = RDB->SymbolicLinkReparseBuffer.PathBuffer; /* (PWCHAR)((PUCHAR)& + RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset); */ Ext2OEMToUnicode(Vcb, &UniName, &OemName); RtlMoveMemory( (PUCHAR)RDB->SymbolicLinkReparseBuffer.PathBuffer + RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset, UniName.Buffer, UniName.Length); Status = STATUS_SUCCESS; } _SEH2_FINALLY { if (OemNameBuffer) { Ext2FreePool(OemNameBuffer, 'NL2E'); } if (!_SEH2_AbnormalTermination()) { if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { Status = Ext2QueueRequest(IrpContext); } else { Ext2CompleteIrpContext(IrpContext, Status); } } } _SEH2_END; return Status; } NTSTATUS Ext2WriteSymlink ( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, IN PEXT2_MCB Mcb, IN PVOID Buffer, IN ULONG Size, OUT PULONG BytesWritten ) { NTSTATUS Status = STATUS_SUCCESS; PUCHAR Data = (PUCHAR)(&Mcb->Inode.i_block[0]); if (Size >= EXT2_LINKLEN_IN_INODE) { /* initialize inode i_block[] */ if (0 == Mcb->Inode.i_blocks) { memset(Data, 0, EXT2_LINKLEN_IN_INODE); ClearFlag(Mcb->Inode.i_flags, EXT4_EXTENTS_FL); Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); } Status = Ext2WriteInode(IrpContext, Vcb, Mcb, 0, Buffer, Size, FALSE, BytesWritten); if (!NT_SUCCESS(Status)) { goto out; } } else { /* free inode blocks before writing in line */ if (Mcb->Inode.i_blocks) { LARGE_INTEGER Zero = {0, 0}; Ext2TruncateFile(IrpContext, Vcb, Mcb, &Zero); } ClearFlag(Mcb->Inode.i_flags, EXT4_EXTENTS_FL); memset(Data, 0, EXT2_LINKLEN_IN_INODE); RtlCopyMemory(Data, Buffer, Size); } Mcb->Inode.i_size = Size; Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); if (BytesWritten) { *BytesWritten = Size; } out: return Status; } NTSTATUS Ext2SetReparsePoint (IN PEXT2_IRP_CONTEXT IrpContext) { PIRP Irp = NULL; PIO_STACK_LOCATION IrpSp; PDEVICE_OBJECT DeviceObject; PEXT2_VCB Vcb = NULL; PEXT2_FCB Fcb = NULL; PEXT2_CCB Ccb = NULL; PEXT2_MCB Mcb = NULL; NTSTATUS Status = STATUS_UNSUCCESSFUL; PVOID InputBuffer; ULONG InputBufferLength; ULONG BytesWritten = 0; PEXT2_FCB ParentDcb = NULL; /* Dcb of it's current parent */ PEXT2_MCB ParentMcb = NULL; PREPARSE_DATA_BUFFER RDB; UNICODE_STRING UniName; OEM_STRING OemName; PCHAR OemNameBuffer = NULL; int OemNameLength = 0, i; BOOLEAN MainResourceAcquired = FALSE; BOOLEAN FcbLockAcquired = FALSE; _SEH2_TRY { Ccb = IrpContext->Ccb; ASSERT(Ccb != NULL); ASSERT((Ccb->Identifier.Type == EXT2CCB) && (Ccb->Identifier.Size == sizeof(EXT2_CCB))); DeviceObject = IrpContext->DeviceObject; Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; Fcb = IrpContext->Fcb; Mcb = Fcb->Mcb; Irp = IrpContext->Irp; IrpSp = IoGetCurrentIrpStackLocation(Irp); ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); FcbLockAcquired = TRUE; ParentMcb = Mcb->Parent; ParentDcb = ParentMcb->Fcb; if (ParentDcb == NULL) { ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb); } if (ParentDcb) { Ext2ReferXcb(&ParentDcb->ReferenceCount); } if (!Mcb) _SEH2_LEAVE; if (FcbLockAcquired) { ExReleaseResourceLite(&Vcb->FcbLock); FcbLockAcquired = FALSE; } if (!ExAcquireResourceSharedLite( &Fcb->MainResource, IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) )) { Status = STATUS_PENDING; _SEH2_LEAVE; } MainResourceAcquired = TRUE; InputBuffer = Irp->AssociatedIrp.SystemBuffer; InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; RDB = (PREPARSE_DATA_BUFFER)InputBuffer; Status = Ext2InspectReparseData(RDB, InputBufferLength); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } UniName.Length = RDB->SymbolicLinkReparseBuffer.SubstituteNameLength; UniName.MaximumLength = UniName.Length; UniName.Buffer = (PWCHAR)((PUCHAR)&RDB->SymbolicLinkReparseBuffer.PathBuffer + RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset); OemNameLength = Ext2UnicodeToOEMSize(Vcb, &UniName); if (OemNameLength > USHRT_MAX) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } OemName.Length = (USHORT)OemNameLength; OemName.MaximumLength = OemNameLength + 1; OemNameBuffer = OemName.Buffer = Ext2AllocatePool(PagedPool, OemName.MaximumLength, 'NL2E'); if (!OemNameBuffer) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } Ext2UnicodeToOEM(Vcb, &OemName, &UniName); OemName.Buffer[OemName.Length] = '\0'; for (i = 0;i < OemName.Length;i++) { if (OemName.Buffer[i] == '\\') { OemName.Buffer[i] = '/'; } } /* free all data blocks of the inode (to be set as symlink) */ { LARGE_INTEGER zero = {0}; Status = Ext2TruncateFile(IrpContext, Vcb, Mcb, &zero); } /* decrease dir count of group desc and vcb stat */ if (S_ISDIR(Mcb->Inode.i_mode)) { ULONG group = (Mcb->Inode.i_ino - 1) / INODES_PER_GROUP; Ext2UpdateGroupDirStat(IrpContext, Vcb, group); /* drop extra reference for dir inode */ ext3_dec_count(&Mcb->Inode); } /* overwrite inode mode as type SYMLINK */ Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_REPARSE_POINT); Status = Ext2WriteSymlink(IrpContext, Vcb, Mcb, OemNameBuffer, OemNameLength, &BytesWritten); if (NT_SUCCESS(Status)) { Ext2SetFileType(IrpContext, Vcb, ParentDcb, Mcb, S_IFLNK | S_IRWXUGO); } } _SEH2_FINALLY { if (FcbLockAcquired) { ExReleaseResourceLite(&Vcb->FcbLock); FcbLockAcquired = FALSE; } if (MainResourceAcquired) { ExReleaseResourceLite(&Fcb->MainResource); } if (OemNameBuffer) { Ext2FreePool(OemNameBuffer, 'NL2E'); } if (NT_SUCCESS(Status)) { Ext2NotifyReportChange( IrpContext, Vcb, Mcb, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED ); } if (!_SEH2_AbnormalTermination()) { if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { Status = Ext2QueueRequest(IrpContext); } else { Ext2CompleteIrpContext(IrpContext, Status); } } if (ParentDcb) { Ext2ReleaseFcb(ParentDcb); } } _SEH2_END; return Status; } NTSTATUS Ext2TruncateSymlink( PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb, PEXT2_MCB Mcb, ULONG Size ) { NTSTATUS status = STATUS_SUCCESS; PUCHAR data = (PUCHAR)&Mcb->Inode.i_block; ULONG len = (ULONG)Mcb->Inode.i_size; LARGE_INTEGER NewSize; if (len < EXT2_LINKLEN_IN_INODE && !Mcb->Inode.i_blocks) { RtlZeroMemory(data + Size, EXT2_LINKLEN_IN_INODE - Size); Mcb->Inode.i_size = Size; Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); } else { NewSize.QuadPart = Size; status = Ext2TruncateFile(IrpContext, Vcb, Mcb, &NewSize); if (!NT_SUCCESS(status)) { goto out; } } out: return status; } /* FIXME: We can only handle one reparse point right now. */ NTSTATUS Ext2DeleteReparsePoint (IN PEXT2_IRP_CONTEXT IrpContext) { PIRP Irp = NULL; PDEVICE_OBJECT DeviceObject; PEXT2_VCB Vcb = NULL; PEXT2_FCB Fcb = NULL; PEXT2_CCB Ccb = NULL; PEXT2_MCB Mcb = NULL; PEXT2_FCB ParentDcb = NULL; /* Dcb of it's current parent */ PEXT2_MCB ParentMcb = NULL; NTSTATUS Status = STATUS_UNSUCCESSFUL; BOOLEAN FcbLockAcquired = FALSE; BOOLEAN MainResourceAcquired = FALSE; _SEH2_TRY { Ccb = IrpContext->Ccb; ASSERT(Ccb != NULL); ASSERT((Ccb->Identifier.Type == EXT2CCB) && (Ccb->Identifier.Size == sizeof(EXT2_CCB))); DeviceObject = IrpContext->DeviceObject; Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; Mcb = IrpContext->Fcb->Mcb; Irp = IrpContext->Irp; ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); FcbLockAcquired = TRUE; ParentMcb = Mcb->Parent; ParentDcb = ParentMcb->Fcb; if (ParentDcb == NULL) { ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb); } if (ParentDcb) { Ext2ReferXcb(&ParentDcb->ReferenceCount); } if (!Mcb || !IsInodeSymLink(&Mcb->Inode) || !IsFlagOn(Ccb->Flags, CCB_OPEN_REPARSE_POINT)) { Status = STATUS_NOT_A_REPARSE_POINT; _SEH2_LEAVE; } Fcb = Ext2AllocateFcb (Vcb, Mcb); if (Fcb) { Ext2ReferXcb(&Fcb->ReferenceCount); } else { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } if (FcbLockAcquired) { ExReleaseResourceLite(&Vcb->FcbLock); FcbLockAcquired = FALSE; } if (!ExAcquireResourceSharedLite( &Fcb->MainResource, IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) )) { Status = STATUS_PENDING; _SEH2_LEAVE; } MainResourceAcquired = TRUE; Status = Ext2TruncateSymlink(IrpContext, Vcb, Mcb, 0); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } /* inode is to be removed */ SetFlag(Ccb->Flags, CCB_DELETE_ON_CLOSE); } _SEH2_FINALLY { if (FcbLockAcquired) { ExReleaseResourceLite(&Vcb->FcbLock); } if (MainResourceAcquired) { ExReleaseResourceLite(&Fcb->MainResource); } if (NT_SUCCESS(Status)) { Ext2NotifyReportChange( IrpContext, Vcb, Mcb, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED ); } if (!_SEH2_AbnormalTermination()) { if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { Status = Ext2QueueRequest(IrpContext); } else { Ext2CompleteIrpContext(IrpContext, Status); } } if (ParentDcb) { Ext2ReleaseFcb(ParentDcb); } if (Fcb) { Ext2ReleaseFcb(Fcb); } } _SEH2_END; return Status; } NTSTATUS Ext2UserFsRequest (IN PEXT2_IRP_CONTEXT IrpContext) { PIRP Irp; PIO_STACK_LOCATION IoStackLocation; ULONG FsControlCode; NTSTATUS Status; ASSERT(IrpContext); ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); Irp = IrpContext->Irp; IoStackLocation = IoGetCurrentIrpStackLocation(Irp); #ifndef _GNU_NTIFS_ FsControlCode = IoStackLocation->Parameters.FileSystemControl.FsControlCode; #else FsControlCode = ((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Parameters.FileSystemControl.FsControlCode; #endif switch (FsControlCode) { case FSCTL_GET_REPARSE_POINT: Status = Ext2GetReparsePoint(IrpContext); break; case FSCTL_SET_REPARSE_POINT: Status = Ext2SetReparsePoint(IrpContext); break; case FSCTL_DELETE_REPARSE_POINT: Status = Ext2DeleteReparsePoint(IrpContext); break; case FSCTL_LOCK_VOLUME: Status = Ext2LockVolume(IrpContext); break; case FSCTL_UNLOCK_VOLUME: Status = Ext2UnlockVolume(IrpContext); break; case FSCTL_DISMOUNT_VOLUME: Status = Ext2DismountVolume(IrpContext); break; case FSCTL_IS_VOLUME_MOUNTED: Status = Ext2IsVolumeMounted(IrpContext); break; case FSCTL_INVALIDATE_VOLUMES: Status = Ext2InvalidateVolumes(IrpContext); break; #if (_WIN32_WINNT >= 0x0500) case FSCTL_ALLOW_EXTENDED_DASD_IO: Status = Ext2AllowExtendedDasdIo(IrpContext); break; #endif //(_WIN32_WINNT >= 0x0500) case FSCTL_REQUEST_OPLOCK_LEVEL_1: case FSCTL_REQUEST_OPLOCK_LEVEL_2: case FSCTL_REQUEST_BATCH_OPLOCK: case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: case FSCTL_OPBATCH_ACK_CLOSE_PENDING: case FSCTL_OPLOCK_BREAK_NOTIFY: case FSCTL_OPLOCK_BREAK_ACK_NO_2: Status = Ext2OplockRequest(IrpContext); break; case FSCTL_IS_VOLUME_DIRTY: Status = Ext2IsVolumeDirty(IrpContext); break; case FSCTL_QUERY_RETRIEVAL_POINTERS: Status = Ext2QueryRetrievalPointers(IrpContext); break; case FSCTL_GET_RETRIEVAL_POINTERS: Status = Ext2GetRetrievalPointers(IrpContext); break; case FSCTL_GET_RETRIEVAL_POINTER_BASE: Status = Ext2GetRetrievalPointerBase(IrpContext); break; default: DEBUG(DL_INF, ( "Ext2UserFsRequest: Invalid User Request: %xh.\n", FsControlCode)); Status = STATUS_INVALID_DEVICE_REQUEST; Ext2CompleteIrpContext(IrpContext, Status); } return Status; } BOOLEAN Ext2IsMediaWriteProtected ( IN PEXT2_IRP_CONTEXT IrpContext, IN PDEVICE_OBJECT TargetDevice ) { PIRP Irp; KEVENT Event; NTSTATUS Status; IO_STATUS_BLOCK IoStatus; KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE, TargetDevice, NULL, 0, NULL, 0, FALSE, &Event, &IoStatus ); if (Irp == NULL) { return FALSE; } SetFlag(IoGetNextIrpStackLocation(Irp)->Flags, SL_OVERRIDE_VERIFY_VOLUME); Status = IoCallDriver(TargetDevice, Irp); if (Status == STATUS_PENDING) { (VOID) KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL ); Status = IoStatus.Status; } return (BOOLEAN)(Status == STATUS_MEDIA_WRITE_PROTECTED); } NTSTATUS Ext2MountVolume (IN PEXT2_IRP_CONTEXT IrpContext) { PDEVICE_OBJECT MainDeviceObject; BOOLEAN GlobalDataResourceAcquired = FALSE; PIRP Irp; PIO_STACK_LOCATION IoStackLocation; PDEVICE_OBJECT TargetDeviceObject; NTSTATUS Status = STATUS_UNRECOGNIZED_VOLUME; PDEVICE_OBJECT VolumeDeviceObject = NULL; PEXT2_VCB Vcb = NULL, OldVcb = NULL; PVPB OldVpb = NULL, Vpb = NULL; PEXT2_SUPER_BLOCK Ext2Sb = NULL; ULONG dwBytes; DISK_GEOMETRY DiskGeometry; _SEH2_TRY { ASSERT(IrpContext != NULL); ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); MainDeviceObject = IrpContext->DeviceObject; // // Make sure we can wait. // SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); // // This request is only allowed on the main device object // if (!IsExt2FsDevice(MainDeviceObject)) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } if (IsFlagOn(Ext2Global->Flags, EXT2_UNLOAD_PENDING)) { Status = STATUS_UNRECOGNIZED_VOLUME; _SEH2_LEAVE; } #if 0 if (IrpContext->RealDevice->Size >= sizeof(ULONG) + sizeof(DEVICE_OBJECT) && *((PULONG)IrpContext->RealDevice->DeviceExtension) == 'DSSA') { } else { Status = STATUS_UNRECOGNIZED_VOLUME; _SEH2_LEAVE; } #endif Irp = IrpContext->Irp; IoStackLocation = IoGetCurrentIrpStackLocation(Irp); TargetDeviceObject = IoStackLocation->Parameters.MountVolume.DeviceObject; dwBytes = sizeof(DISK_GEOMETRY); Status = Ext2DiskIoControl( TargetDeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DiskGeometry, &dwBytes ); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } Status = IoCreateDevice( MainDeviceObject->DriverObject, sizeof(EXT2_VCB), NULL, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &VolumeDeviceObject ); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } INC_MEM_COUNT(PS_VCB, VolumeDeviceObject, sizeof(EXT2_VCB)); #ifdef _PNP_POWER_ /* don't care about power management requests */ VolumeDeviceObject->DeviceObjectExtension->PowerControlNeeded = FALSE; #endif VolumeDeviceObject->StackSize = (CCHAR)(TargetDeviceObject->StackSize + 1); ClearFlag(VolumeDeviceObject->Flags, DO_DEVICE_INITIALIZING); /* These are for buffer-address alignment requirements. Never do this check, unless you want fail user requests :) if (TargetDeviceObject->AlignmentRequirement > VolumeDeviceObject->AlignmentRequirement) { VolumeDeviceObject->AlignmentRequirement = TargetDeviceObject->AlignmentRequirement; } if (DiskGeometry.BytesPerSector - 1 > VolumeDeviceObject->AlignmentRequirement) { VolumeDeviceObject->AlignmentRequirement = DiskGeometry.BytesPerSector - 1; TargetDeviceObject->AlignmentRequirement = DiskGeometry.BytesPerSector - 1; } */ (IoStackLocation->Parameters.MountVolume.Vpb)->DeviceObject = VolumeDeviceObject; Vpb = IoStackLocation->Parameters.MountVolume.Vpb; Vcb = (PEXT2_VCB) VolumeDeviceObject->DeviceExtension; RtlZeroMemory(Vcb, sizeof(EXT2_VCB)); Vcb->Identifier.Type = EXT2VCB; Vcb->Identifier.Size = sizeof(EXT2_VCB); Vcb->TargetDeviceObject = TargetDeviceObject; Vcb->DiskGeometry = DiskGeometry; InitializeListHead(&Vcb->Next); Status = Ext2LoadSuper(Vcb, FALSE, &Ext2Sb); if (!NT_SUCCESS(Status)) { Vcb = NULL; Status = STATUS_UNRECOGNIZED_VOLUME; _SEH2_LEAVE; } ASSERT (NULL != Ext2Sb); /* check Linux Ext2/Ext3 volume magic */ if (Ext2Sb->s_magic == EXT2_SUPER_MAGIC) { DEBUG(DL_INF, ( "Volume of ext2 file system is found.\n")); } else { Status = STATUS_UNRECOGNIZED_VOLUME; Vcb = NULL; _SEH2_LEAVE; } DEBUG(DL_DBG, ("Ext2MountVolume: DevObject=%p Vcb=%p\n", VolumeDeviceObject, Vcb)); /* initialize Vcb structure */ Status = Ext2InitializeVcb( IrpContext, Vcb, Ext2Sb, TargetDeviceObject, VolumeDeviceObject, Vpb); if (NT_SUCCESS(Status)) { PLIST_ENTRY List; ExAcquireResourceExclusiveLite(&(Ext2Global->Resource), TRUE); GlobalDataResourceAcquired = TRUE; for (List = Ext2Global->VcbList.Flink; List != &Ext2Global->VcbList; List = List->Flink) { OldVcb = CONTAINING_RECORD(List, EXT2_VCB, Next); OldVpb = OldVcb->Vpb; /* in case we are already in the queue, should not happen */ if (OldVpb == Vpb) { continue; } if ( (OldVpb->SerialNumber == Vpb->SerialNumber) && (!IsMounted(OldVcb)) && (IsFlagOn(OldVcb->Flags, VCB_NEW_VPB)) && (OldVpb->RealDevice == TargetDeviceObject) && (OldVpb->VolumeLabelLength == Vpb->VolumeLabelLength) && (RtlEqualMemory(&OldVpb->VolumeLabel[0], &Vpb->VolumeLabel[0], Vpb->VolumeLabelLength)) && (RtlEqualMemory(&OldVcb->SuperBlock->s_uuid[0], &Vcb->SuperBlock->s_uuid[0], 16)) ) { ClearFlag(OldVcb->Flags, VCB_MOUNTED); } } SetLongFlag(Vcb->Flags, VCB_MOUNTED); SetFlag(Vcb->Vpb->Flags, VPB_MOUNTED); Ext2InsertVcb(Vcb); Vcb = NULL; Vpb = NULL; ObDereferenceObject(TargetDeviceObject); } else { Vcb = NULL; } } _SEH2_FINALLY { if (GlobalDataResourceAcquired) { ExReleaseResourceLite(&Ext2Global->Resource); } if (!NT_SUCCESS(Status)) { if (!NT_SUCCESS(Status)) { if ( Vpb != NULL ) { Vpb->DeviceObject = NULL; } } if (Vcb) { Ext2DestroyVcb(Vcb); } else { if (Ext2Sb) { Ext2FreePool(Ext2Sb, EXT2_SB_MAGIC); } if (VolumeDeviceObject) { IoDeleteDevice(VolumeDeviceObject); DEC_MEM_COUNT(PS_VCB, VolumeDeviceObject, sizeof(EXT2_VCB)); } } } if (!IrpContext->ExceptionInProgress) { Ext2CompleteIrpContext(IrpContext, Status); } } _SEH2_END; return Status; } VOID Ext2VerifyVcb (IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb ) { NTSTATUS Status = STATUS_SUCCESS; BOOLEAN bVerify = FALSE; ULONG ChangeCount = 0; ULONG dwBytes; PIRP Irp; PEXTENDED_IO_STACK_LOCATION IrpSp; _SEH2_TRY { ASSERT(IrpContext != NULL); ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); Irp = IrpContext->Irp; IrpSp = (PEXTENDED_IO_STACK_LOCATION)IoGetCurrentIrpStackLocation(Irp); bVerify = IsFlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); if ( (IsFlagOn(Vcb->Flags, VCB_REMOVABLE_MEDIA) || IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) && !bVerify ) { dwBytes = sizeof(ULONG); Status = Ext2DiskIoControl( Vcb->TargetDeviceObject, IOCTL_DISK_CHECK_VERIFY, NULL, 0, &ChangeCount, &dwBytes ); if ( STATUS_VERIFY_REQUIRED == Status || STATUS_DEVICE_NOT_READY == Status || STATUS_NO_MEDIA_IN_DEVICE == Status || (NT_SUCCESS(Status) && (ChangeCount != Vcb->ChangeCount))) { KIRQL Irql; IoAcquireVpbSpinLock(&Irql); if (Vcb->Vpb == Vcb->Vpb->RealDevice->Vpb) { SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); } IoReleaseVpbSpinLock(Irql); } else { if (!NT_SUCCESS(Status)) { Ext2NormalizeAndRaiseStatus(IrpContext, Status); } } } if ( IsFlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) { IoSetHardErrorOrVerifyDevice( Irp, Vcb->Vpb->RealDevice ); Ext2NormalizeAndRaiseStatus ( IrpContext, STATUS_VERIFY_REQUIRED ); } if (IsMounted(Vcb)) { if ( (IrpContext->MajorFunction == IRP_MJ_WRITE) || (IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION) || (IrpContext->MajorFunction == IRP_MJ_SET_EA) || (IrpContext->MajorFunction == IRP_MJ_FLUSH_BUFFERS) || (IrpContext->MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION) || (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST && IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_MARK_VOLUME_DIRTY)) { if (IsFlagOn(Vcb->Flags, VCB_WRITE_PROTECTED)) { KIRQL Irql; IoAcquireVpbSpinLock(&Irql); if (Vcb->Vpb == Vcb->Vpb->RealDevice->Vpb) { SetFlag (Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); } IoReleaseVpbSpinLock(Irql); IoSetHardErrorOrVerifyDevice( Irp, Vcb->Vpb->RealDevice ); Ext2RaiseStatus(IrpContext, STATUS_MEDIA_WRITE_PROTECTED); } } } } _SEH2_FINALLY { } _SEH2_END; } NTSTATUS Ext2VerifyVolume (IN PEXT2_IRP_CONTEXT IrpContext) { PDEVICE_OBJECT DeviceObject; NTSTATUS Status = STATUS_UNSUCCESSFUL; PEXT2_SUPER_BLOCK ext2_sb = NULL; PEXT2_VCB Vcb = NULL; BOOLEAN VcbResourceAcquired = FALSE; PIRP Irp; ULONG ChangeCount = 0; ULONG dwBytes; _SEH2_TRY { ASSERT(IrpContext != NULL); ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; // // This request is not allowed on the main device object // if (IsExt2FsDevice(DeviceObject)) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); VcbResourceAcquired = ExAcquireResourceExclusiveLite( &Vcb->MainResource, TRUE ); if (!FlagOn(Vcb->TargetDeviceObject->Flags, DO_VERIFY_VOLUME)) { Status = STATUS_SUCCESS; _SEH2_LEAVE; } if (!IsMounted(Vcb)) { Status = STATUS_WRONG_VOLUME; _SEH2_LEAVE; } dwBytes = sizeof(ULONG); Status = Ext2DiskIoControl( Vcb->TargetDeviceObject, IOCTL_DISK_CHECK_VERIFY, NULL, 0, &ChangeCount, &dwBytes ); if (!NT_SUCCESS(Status)) { Status = STATUS_WRONG_VOLUME; _SEH2_LEAVE; } else { Vcb->ChangeCount = ChangeCount; } Irp = IrpContext->Irp; Status = Ext2LoadSuper(Vcb, TRUE, &ext2_sb); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } ASSERT(NULL != ext2_sb); if ((ext2_sb->s_magic == EXT2_SUPER_MAGIC) && (memcmp(ext2_sb->s_uuid, SUPER_BLOCK->s_uuid, 16) == 0) && (memcmp(ext2_sb->s_volume_name, SUPER_BLOCK->s_volume_name, 16) ==0)) { ClearFlag(Vcb->TargetDeviceObject->Flags, DO_VERIFY_VOLUME); if (Ext2IsMediaWriteProtected(IrpContext, Vcb->TargetDeviceObject)) { SetLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); } else { ClearLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); } DEBUG(DL_INF, ( "Ext2VerifyVolume: Volume verify succeeded.\n")); } else { Status = STATUS_WRONG_VOLUME; Ext2PurgeVolume(Vcb, FALSE); SetLongFlag(Vcb->Flags, VCB_DISMOUNT_PENDING); ClearFlag(Vcb->TargetDeviceObject->Flags, DO_VERIFY_VOLUME); DEBUG(DL_INF, ( "Ext2VerifyVolume: Volume verify failed.\n")); } } _SEH2_FINALLY { if (ext2_sb) Ext2FreePool(ext2_sb, EXT2_SB_MAGIC); if (VcbResourceAcquired) { ExReleaseResourceLite(&Vcb->MainResource); } if (!IrpContext->ExceptionInProgress) { Ext2CompleteIrpContext(IrpContext, Status); } } _SEH2_END; return Status; } NTSTATUS Ext2IsVolumeMounted (IN PEXT2_IRP_CONTEXT IrpContext) { PDEVICE_OBJECT DeviceObject; PEXT2_VCB Vcb = 0; NTSTATUS Status = STATUS_SUCCESS; ASSERT(IrpContext); ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(IsMounted(Vcb)); Ext2VerifyVcb (IrpContext, Vcb); Ext2CompleteIrpContext(IrpContext, Status); return Status; } NTSTATUS Ext2DismountVolume (IN PEXT2_IRP_CONTEXT IrpContext) { PDEVICE_OBJECT DeviceObject; NTSTATUS Status = STATUS_UNSUCCESSFUL; PEXT2_VCB Vcb = NULL; BOOLEAN VcbResourceAcquired = FALSE; _SEH2_TRY { ASSERT(IrpContext != NULL); ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; // // This request is not allowed on the main device object // if (IsExt2FsDevice(DeviceObject)) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); ASSERT(IsMounted(Vcb)); ExAcquireResourceExclusiveLite( &Vcb->MainResource, TRUE ); VcbResourceAcquired = TRUE; if ( IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) { Status = STATUS_VOLUME_DISMOUNTED; _SEH2_LEAVE; } Ext2FlushFiles(IrpContext, Vcb, FALSE); Ext2FlushVolume(IrpContext, Vcb, FALSE); ExReleaseResourceLite(&Vcb->MainResource); VcbResourceAcquired = FALSE; Ext2PurgeVolume(Vcb, TRUE); Ext2CheckDismount(IrpContext, Vcb, TRUE); DEBUG(DL_INF, ( "Ext2Dismount: Volume dismount pending.\n")); Status = STATUS_SUCCESS; } _SEH2_FINALLY { if (VcbResourceAcquired) { ExReleaseResourceLite(&Vcb->MainResource); } if (!IrpContext->ExceptionInProgress) { Ext2CompleteIrpContext(IrpContext, Status); } } _SEH2_END; return Status; } BOOLEAN Ext2CheckDismount ( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, IN BOOLEAN bForce ) { KIRQL Irql; PVPB Vpb = Vcb->Vpb, NewVpb = NULL; BOOLEAN bDeleted = FALSE, bTearDown = FALSE; ULONG UnCleanCount = 0; NewVpb = ExAllocatePoolWithTag(NonPagedPool, VPB_SIZE, TAG_VPB); if (NewVpb == NULL) { DEBUG(DL_ERR, ( "Ex2CheckDismount: failed to allocate NewVpb.\n")); return FALSE; } DEBUG(DL_DBG, ("Ext2CheckDismount: NewVpb allocated: %p\n", NewVpb)); INC_MEM_COUNT(PS_VPB, NewVpb, sizeof(VPB)); memset(NewVpb, '_', VPB_SIZE); RtlZeroMemory(NewVpb, sizeof(VPB)); ExAcquireResourceExclusiveLite( &Ext2Global->Resource, TRUE ); ExAcquireResourceExclusiveLite( &Vcb->MainResource, TRUE ); if (IrpContext && IrpContext->MajorFunction == IRP_MJ_CREATE && IrpContext->RealDevice == Vcb->RealDevice) { UnCleanCount = 2; } else { UnCleanCount = 1; } IoAcquireVpbSpinLock (&Irql); DEBUG(DL_DBG, ("Ext2CheckDismount: Vpb %p ioctl=%d Device %p\n", Vpb, Vpb->ReferenceCount, Vpb->RealDevice)); if (Vpb->ReferenceCount <= UnCleanCount) { if (!IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) { ClearFlag(Vpb->Flags, VPB_MOUNTED); ClearFlag(Vpb->Flags, VPB_LOCKED); if ((Vcb->RealDevice != Vpb->RealDevice) && (Vcb->RealDevice->Vpb == Vpb)) { SetFlag(Vcb->RealDevice->Flags, DO_DEVICE_INITIALIZING); SetFlag(Vpb->Flags, VPB_PERSISTENT ); } Ext2RemoveVcb(Vcb); SetLongFlag(Vcb->Flags, VCB_DISMOUNT_PENDING); } if (Vpb->ReferenceCount) { bTearDown = TRUE; } else { bDeleted = TRUE; Vpb->DeviceObject = NULL; } DEBUG(DL_DBG, ("Ext2CheckDismount: Vpb: %p bDeleted=%d bTearDown=%d\n", Vpb, bDeleted, bTearDown)); } else if (bForce) { DEBUG(DL_DBG, ( "Ext2CheckDismount: New/Old Vpb %p/%p Realdevice = %p\n", NewVpb, Vcb->Vpb, Vpb->RealDevice)); /* keep vpb president and later we'll free it */ SetFlag(Vpb->Flags, VPB_PERSISTENT); Vcb->Vpb2 = Vcb->Vpb; NewVpb->Type = IO_TYPE_VPB; NewVpb->Size = sizeof(VPB); NewVpb->Flags = Vpb->Flags & VPB_REMOVE_PENDING; NewVpb->RealDevice = Vpb->RealDevice; NewVpb->RealDevice->Vpb = NewVpb; NewVpb = NULL; ClearFlag(Vpb->Flags, VPB_MOUNTED); SetLongFlag(Vcb->Flags, VCB_NEW_VPB); ClearLongFlag(Vcb->Flags, VCB_MOUNTED); } IoReleaseVpbSpinLock(Irql); ExReleaseResourceLite(&Vcb->MainResource); ExReleaseResourceLite(&Ext2Global->Resource); if (bTearDown) { DEBUG(DL_DBG, ( "Ext2CheckDismount: Tearing vcb %p ...\n", Vcb)); Ext2TearDownStream(Vcb); } if (bDeleted) { DEBUG(DL_DBG, ( "Ext2CheckDismount: Deleting vcb %p ...\n", Vcb)); Ext2DestroyVcb(Vcb); } if (NewVpb != NULL) { DEBUG(DL_DBG, ( "Ext2CheckDismount: freeing new Vpb %p\n", NewVpb)); ExFreePoolWithTag(NewVpb, TAG_VPB); DEC_MEM_COUNT(PS_VPB, NewVpb, sizeof(VPB)); } return bDeleted; } NTSTATUS Ext2PurgeVolume (IN PEXT2_VCB Vcb, IN BOOLEAN FlushBeforePurge ) { PEXT2_FCB Fcb; LIST_ENTRY List, *Next; BOOLEAN VcbResourceAcquired = FALSE; BOOLEAN FcbResourceAcquired = FALSE; BOOLEAN gdResourceAcquired = FALSE; _SEH2_TRY { ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == EXT2VCB) && (Vcb->Identifier.Size == sizeof(EXT2_VCB))); ExAcquireResourceExclusiveLite(&Vcb->MainResource, TRUE); VcbResourceAcquired = TRUE; if (IsVcbReadOnly(Vcb)) { FlushBeforePurge = FALSE; } InitializeListHead(&List); ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); FcbResourceAcquired = TRUE; while (!IsListEmpty(&Vcb->FcbList)) { Next = RemoveHeadList(&Vcb->FcbList); Fcb = CONTAINING_RECORD(Next, EXT2_FCB, Next); DEBUG(DL_INF, ( "Ext2PurgeVolume: %wZ refercount=%xh\n", &Fcb->Mcb->FullName, Fcb->ReferenceCount)); InsertTailList(&List, &Fcb->Next); } while (!IsListEmpty(&List)) { Next = RemoveHeadList(&List); Fcb = CONTAINING_RECORD(Next, EXT2_FCB, Next); if (ExAcquireResourceExclusiveLite( &Fcb->MainResource, TRUE )) { Ext2PurgeFile(Fcb, FlushBeforePurge); if (Fcb->ReferenceCount <= 1) { Fcb->TsDrop.QuadPart = 0; InsertHeadList(&Vcb->FcbList, &Fcb->Next); } else { InsertTailList(&Vcb->FcbList, &Fcb->Next); } ExReleaseResourceLite(&Fcb->MainResource); } } if (FcbResourceAcquired) { ExReleaseResourceLite(&Vcb->FcbLock); FcbResourceAcquired = FALSE; } /* acquire bd lock to avoid bh creation */ ExAcquireResourceExclusiveLite(&Vcb->sbi.s_gd_lock, TRUE); gdResourceAcquired = TRUE; /* discard buffer_headers for group_desc */ Ext2DropBH(Vcb); if (FlushBeforePurge) { ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE); ExReleaseResourceLite(&Vcb->PagingIoResource); CcFlushCache(&Vcb->SectionObject, NULL, 0, NULL); } if (Vcb->SectionObject.ImageSectionObject) { MmFlushImageSection(&Vcb->SectionObject, MmFlushForWrite); } if (Vcb->SectionObject.DataSectionObject) { CcPurgeCacheSection(&Vcb->SectionObject, NULL, 0, FALSE); } DEBUG(DL_INF, ( "Ext2PurgeVolume: Volume flushed and purged.\n")); } _SEH2_FINALLY { if (gdResourceAcquired) { ExReleaseResourceLite(&Vcb->sbi.s_gd_lock); } if (FcbResourceAcquired) { ExReleaseResourceLite(&Vcb->FcbLock); } if (VcbResourceAcquired) { ExReleaseResourceLite(&Vcb->MainResource); } } _SEH2_END; return STATUS_SUCCESS; } NTSTATUS Ext2PurgeFile ( IN PEXT2_FCB Fcb, IN BOOLEAN FlushBeforePurge ) { IO_STATUS_BLOCK IoStatus; ASSERT(Fcb != NULL); ASSERT((Fcb->Identifier.Type == EXT2FCB) && (Fcb->Identifier.Size == sizeof(EXT2_FCB))); if (!IsVcbReadOnly(Fcb->Vcb) && FlushBeforePurge) { DEBUG(DL_INF, ( "Ext2PurgeFile: CcFlushCache on %wZ.\n", &Fcb->Mcb->FullName)); ExAcquireSharedStarveExclusive(&Fcb->PagingIoResource, TRUE); ExReleaseResourceLite(&Fcb->PagingIoResource); CcFlushCache(&Fcb->SectionObject, NULL, 0, &IoStatus); ClearFlag(Fcb->Flags, FCB_FILE_MODIFIED); } if (Fcb->SectionObject.ImageSectionObject) { DEBUG(DL_INF, ( "Ext2PurgeFile: MmFlushImageSection on %wZ.\n", &Fcb->Mcb->FullName)); MmFlushImageSection(&Fcb->SectionObject, MmFlushForWrite); } if (Fcb->SectionObject.DataSectionObject) { DEBUG(DL_INF, ( "Ext2PurgeFile: CcPurgeCacheSection on %wZ.\n", &Fcb->Mcb->FullName)); CcPurgeCacheSection(&Fcb->SectionObject, NULL, 0, FALSE); } return STATUS_SUCCESS; } NTSTATUS Ext2FileSystemControl (IN PEXT2_IRP_CONTEXT IrpContext) { NTSTATUS Status; ASSERT(IrpContext); ASSERT((IrpContext->Identifier.Type == EXT2ICX) && (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); switch (IrpContext->MinorFunction) { case IRP_MN_USER_FS_REQUEST: Status = Ext2UserFsRequest(IrpContext); break; case IRP_MN_MOUNT_VOLUME: Status = Ext2MountVolume(IrpContext); break; case IRP_MN_VERIFY_VOLUME: Status = Ext2VerifyVolume(IrpContext); break; default: DEBUG(DL_ERR, ( "Ext2FilsSystemControl: Invalid Device Request.\n")); Status = STATUS_INVALID_DEVICE_REQUEST; Ext2CompleteIrpContext(IrpContext, Status); } return Status; }