Esempio n. 1
0
static NTSTATUS
VfatGetRetrievalPointers(PVFAT_IRP_CONTEXT IrpContext)
{
   PIO_STACK_LOCATION Stack;
   LARGE_INTEGER Vcn;
   PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
   PFILE_OBJECT FileObject;
   ULONG MaxExtentCount;
   PVFATFCB Fcb;
   PDEVICE_EXTENSION DeviceExt;
   ULONG FirstCluster;
   ULONG CurrentCluster;
   ULONG LastCluster;
   NTSTATUS Status;

   DPRINT("VfatGetRetrievalPointers(IrpContext %p)\n", IrpContext);

   DeviceExt = IrpContext->DeviceExt;
   FileObject = IrpContext->FileObject;
   Stack = IrpContext->Stack;
   if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER) ||
       Stack->Parameters.DeviceIoControl.Type3InputBuffer == NULL)
   {
      return STATUS_INVALID_PARAMETER;
   }
   if (IrpContext->Irp->UserBuffer == NULL ||
       Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))
   {
      return STATUS_BUFFER_TOO_SMALL;
   }

   Fcb = FileObject->FsContext;

   ExAcquireResourceSharedLite(&Fcb->MainResource, TRUE);

   Vcn = ((PSTARTING_VCN_INPUT_BUFFER)Stack->Parameters.DeviceIoControl.Type3InputBuffer)->StartingVcn;
   RetrievalPointers = IrpContext->Irp->UserBuffer;

   MaxExtentCount = ((Stack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(RetrievalPointers->ExtentCount) - sizeof(RetrievalPointers->StartingVcn)) / sizeof(RetrievalPointers->Extents[0]));


   if (Vcn.QuadPart >= Fcb->RFCB.AllocationSize.QuadPart / DeviceExt->FatInfo.BytesPerCluster)
   {
      Status = STATUS_INVALID_PARAMETER;
      goto ByeBye;
   }

   CurrentCluster = FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
   Status = OffsetToCluster(DeviceExt, FirstCluster,
                            Vcn.u.LowPart * DeviceExt->FatInfo.BytesPerCluster,
                            &CurrentCluster, FALSE);
   if (!NT_SUCCESS(Status))
   {
      goto ByeBye;
   }

   RetrievalPointers->StartingVcn = Vcn;
   RetrievalPointers->ExtentCount = 0;
   RetrievalPointers->Extents[0].Lcn.u.HighPart = 0;
   RetrievalPointers->Extents[0].Lcn.u.LowPart = CurrentCluster - 2;
   LastCluster = 0;
   while (CurrentCluster != 0xffffffff && RetrievalPointers->ExtentCount < MaxExtentCount)
   {

      LastCluster = CurrentCluster;
      Status = NextCluster(DeviceExt, CurrentCluster, &CurrentCluster, FALSE);
      Vcn.QuadPart++;
      if (!NT_SUCCESS(Status))
      {
         goto ByeBye;
      }

      if (LastCluster + 1 != CurrentCluster)
      {
         RetrievalPointers->Extents[RetrievalPointers->ExtentCount].NextVcn = Vcn;
         RetrievalPointers->ExtentCount++;
         if (RetrievalPointers->ExtentCount < MaxExtentCount)
         {
            RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.HighPart = 0;
            RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.LowPart = CurrentCluster - 2;
         }
      }
   }

   IrpContext->Irp->IoStatus.Information = sizeof(RETRIEVAL_POINTERS_BUFFER) + (sizeof(RetrievalPointers->Extents[0]) * (RetrievalPointers->ExtentCount - 1));
   Status = STATUS_SUCCESS;

ByeBye:
   ExReleaseResourceLite(&Fcb->MainResource);

   return Status;
}
Esempio n. 2
0
static
NTSTATUS
VfatWriteFileData(
    PVFAT_IRP_CONTEXT IrpContext,
    ULONG Length,
    LARGE_INTEGER WriteOffset)
{
    PDEVICE_EXTENSION DeviceExt;
    PVFATFCB Fcb;
    ULONG Count;
    ULONG FirstCluster;
    ULONG CurrentCluster;
    ULONG BytesDone;
    ULONG StartCluster;
    ULONG ClusterCount;
    NTSTATUS Status = STATUS_SUCCESS;
    BOOLEAN First = TRUE;
    ULONG BytesPerSector;
    ULONG BytesPerCluster;
    LARGE_INTEGER StartOffset;
    ULONG BufferOffset;
    ULONG LastCluster;
    ULONG LastOffset;

    /* PRECONDITION */
    ASSERT(IrpContext);
    DeviceExt = IrpContext->DeviceExt;
    ASSERT(DeviceExt);
    ASSERT(DeviceExt->FatInfo.BytesPerCluster);
    ASSERT(IrpContext->FileObject);
    ASSERT(IrpContext->FileObject->FsContext2 != NULL);

    Fcb = IrpContext->FileObject->FsContext;
    BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
    BytesPerSector = DeviceExt->FatInfo.BytesPerSector;

    DPRINT("VfatWriteFileData(DeviceExt %p, FileObject %p, "
           "Length %u, WriteOffset 0x%I64x), '%wZ'\n", DeviceExt,
           IrpContext->FileObject, Length, WriteOffset.QuadPart,
           &Fcb->PathNameU);

    ASSERT(WriteOffset.QuadPart + Length <= Fcb->RFCB.AllocationSize.QuadPart);
    ASSERT(WriteOffset.u.LowPart % BytesPerSector == 0);
    ASSERT(Length % BytesPerSector == 0);

    /* Is this a write of the volume? */
    if (Fcb->Flags & FCB_IS_VOLUME)
    {
        Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, TRUE);
        if (!NT_SUCCESS(Status))
        {
            DPRINT1("Volume writing failed, Status %x\n", Status);
        }
        return Status;
    }

    /* Is this a write to the FAT? */
    if (Fcb->Flags & FCB_IS_FAT)
    {
        WriteOffset.u.LowPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
        IrpContext->RefCount = 1;
        for (Count = 0; Count < DeviceExt->FatInfo.FATCount; Count++)
        {
            Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, FALSE);
            if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
            {
                DPRINT1("FAT writing failed, Status %x\n", Status);
                break;
            }
            WriteOffset.u.LowPart += Fcb->RFCB.FileSize.u.LowPart;
        }

        if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
        {
            KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
        }

        if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
        {
            Status = IrpContext->Irp->IoStatus.Status;
        }
        return Status;
    }

    /*
     * Find the first cluster
     */
    FirstCluster =
    CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);

    if (FirstCluster == 1)
    {
        ASSERT(WriteOffset.u.LowPart + Length <= DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector);
        // Directory of FAT12/16 needs a special handling
        WriteOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
        // Fire up the write command
        Status = VfatWriteDiskPartial (IrpContext, &WriteOffset, Length, 0, TRUE);
        return Status;
    }

    ExAcquireFastMutex(&Fcb->LastMutex);
    LastCluster = Fcb->LastCluster;
    LastOffset = Fcb->LastOffset;
    ExReleaseFastMutex(&Fcb->LastMutex);

    /*
     * Find the cluster to start the write from
     */
    if (LastCluster > 0 && WriteOffset.u.LowPart >= LastOffset)
    {
        Status = OffsetToCluster(DeviceExt, LastCluster,
                                 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) -
                                 LastOffset,
                                 &CurrentCluster, FALSE);
#ifdef DEBUG_VERIFY_OFFSET_CACHING
        /* DEBUG VERIFICATION */
        {
            ULONG CorrectCluster;
            OffsetToCluster(DeviceExt, FirstCluster,
                            ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
                            &CorrectCluster, FALSE);
            if (CorrectCluster != CurrentCluster)
                KeBugCheck(FAT_FILE_SYSTEM);
        }
#endif
    }
    else
    {
        Status = OffsetToCluster(DeviceExt, FirstCluster,
                                 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
                                 &CurrentCluster, FALSE);
    }

    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    ExAcquireFastMutex(&Fcb->LastMutex);
    Fcb->LastCluster = CurrentCluster;
    Fcb->LastOffset = ROUND_DOWN (WriteOffset.u.LowPart, BytesPerCluster);
    ExReleaseFastMutex(&Fcb->LastMutex);

    IrpContext->RefCount = 1;
    BufferOffset = 0;

    while (Length > 0 && CurrentCluster != 0xffffffff)
    {
        StartCluster = CurrentCluster;
        StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
        BytesDone = 0;
        ClusterCount = 0;

        do
        {
            ClusterCount++;
            if (First)
            {
                BytesDone =  min (Length, BytesPerCluster - (WriteOffset.u.LowPart % BytesPerCluster));
                StartOffset.QuadPart += WriteOffset.u.LowPart % BytesPerCluster;
                First = FALSE;
            }
            else
            {
                if (Length - BytesDone > BytesPerCluster)
                {
                    BytesDone += BytesPerCluster;
                }
                else
                {
                    BytesDone = Length;
                }
            }
            Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
        }
        while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
        DPRINT("start %08x, next %08x, count %u\n",
               StartCluster, CurrentCluster, ClusterCount);

        ExAcquireFastMutex(&Fcb->LastMutex);
        Fcb->LastCluster = StartCluster + (ClusterCount - 1);
        Fcb->LastOffset = ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
        ExReleaseFastMutex(&Fcb->LastMutex);

        // Fire up the write command
        Status = VfatWriteDiskPartial (IrpContext, &StartOffset, BytesDone, BufferOffset, FALSE);
        if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
        {
            break;
        }
        BufferOffset += BytesDone;
        Length -= BytesDone;
        WriteOffset.u.LowPart += BytesDone;
    }

    if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
    {
        KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
    }

    if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
    {
        if (Length > 0)
        {
            Status = STATUS_UNSUCCESSFUL;
        }
        else
        {
            Status = IrpContext->Irp->IoStatus.Status;
        }
    }
    return Status;
}
Esempio n. 3
0
NTSTATUS
VfatWrite(
    PVFAT_IRP_CONTEXT IrpContext)
{
    PVFATFCB Fcb;
    PERESOURCE Resource = NULL;
    LARGE_INTEGER ByteOffset;
    LARGE_INTEGER OldFileSize;
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG Length = 0;
    PVOID Buffer;
    ULONG BytesPerSector;

    ASSERT(IrpContext);

    DPRINT("VfatWrite(IrpContext %p)\n", IrpContext);

    ASSERT(IrpContext->DeviceObject);

    // This request is not allowed on the main device object
    if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
    {
        DPRINT("VfatWrite is called with the main device object.\n");
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto ByeBye;
    }

    ASSERT(IrpContext->DeviceExt);
    ASSERT(IrpContext->FileObject);
    Fcb = IrpContext->FileObject->FsContext;
    ASSERT(Fcb);

    if (Fcb->Flags & FCB_IS_PAGE_FILE)
    {
        PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
        IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
        IoSkipCurrentIrpStackLocation(IrpContext->Irp);
        DPRINT("Write to page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart);
        Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
        VfatFreeIrpContext(IrpContext);
        return Status;
    }

    DPRINT("<%wZ>\n", &Fcb->PathNameU);

    /* fail if file is a directory and no paged read */
    if (*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
    {
        Status = STATUS_INVALID_PARAMETER;
        goto ByeBye;
    }

    ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
    if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
        ByteOffset.u.HighPart == -1)
    {
        ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
    }
    Length = IrpContext->Stack->Parameters.Write.Length;
    BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;

    if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
    {
        Status = STATUS_INVALID_PARAMETER;
        goto ByeBye;
    }

    if (Fcb->Flags & (FCB_IS_FAT | FCB_IS_VOLUME) ||
        vfatDirEntryGetFirstCluster(IrpContext->DeviceExt, &Fcb->entry) == 1)
    {
        if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
        {
            // we can't extend the FAT, the volume or the root on FAT12/FAT16
            Status = STATUS_END_OF_FILE;
            goto ByeBye;
        }
    }

    if (IrpContext->Irp->Flags & (IRP_PAGING_IO|IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
    {
        if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
        {
            // non cached write must be sector aligned
            Status = STATUS_INVALID_PARAMETER;
            goto ByeBye;
        }
    }

    if (Length == 0)
    {
        /* FIXME: Update last write time */
        IrpContext->Irp->IoStatus.Information = 0;
        Status = STATUS_SUCCESS;
        goto ByeBye;
    }

    if (IrpContext->Irp->Flags & IRP_PAGING_IO)
    {
        if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
        {
            Status = STATUS_INVALID_PARAMETER;
            goto ByeBye;
        }

        if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
        {
            Length =  ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
        }
    }

    if (Fcb->Flags & FCB_IS_VOLUME)
    {
        Resource = &IrpContext->DeviceExt->DirResource;
    }
    else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
    {
        Resource = &Fcb->PagingIoResource;
    }
    else
    {
        Resource = &Fcb->MainResource;
    }

    if (Fcb->Flags & FCB_IS_PAGE_FILE)
    {
        if (!ExAcquireResourceSharedLite(Resource,
                                         (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
        {
            Resource = NULL;
            Status = STATUS_PENDING;
            goto ByeBye;
        }
    }
    else
    {
        if (!ExAcquireResourceExclusiveLite(Resource,
                                            (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
        {
            Resource = NULL;
            Status = STATUS_PENDING;
            goto ByeBye;
        }
    }

    if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
        FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
    {
        if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
        {
            Status = STATUS_FILE_LOCK_CONFLICT;
            goto ByeBye;
        }
    }

    if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT) && !(Fcb->Flags & FCB_IS_VOLUME))
    {
        if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
        {
            Status = STATUS_PENDING;
            goto ByeBye;
        }
    }

    OldFileSize = Fcb->RFCB.FileSize;

    Buffer = VfatGetUserBuffer(IrpContext->Irp);
    if (!Buffer)
    {
        Status = STATUS_INVALID_USER_BUFFER;
        goto ByeBye;
    }


    if (!(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)) &&
        !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
        ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
    {
        LARGE_INTEGER AllocationSize;
        AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
        Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
                                                  IrpContext->DeviceExt, &AllocationSize);
        if (!NT_SUCCESS (Status))
        {
            goto ByeBye;
        }
    }

    if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
        !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
    {
        // cached write

        if (IrpContext->FileObject->PrivateCacheMap == NULL)
        {
            CcInitializeCacheMap(IrpContext->FileObject,
                                 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
                                 FALSE,
                                 &VfatGlobalData->CacheMgrCallbacks,
                                 Fcb);
        }

        if (ByteOffset.QuadPart > OldFileSize.QuadPart)
        {
            CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
        }

        if (CcCopyWrite(IrpContext->FileObject, &ByteOffset, Length,
                        1 /*IrpContext->Flags & IRPCONTEXT_CANWAIT*/, Buffer))
        {
            IrpContext->Irp->IoStatus.Information = Length;
            Status = STATUS_SUCCESS;
        }
        else
        {
            Status = STATUS_UNSUCCESSFUL;
        }
    }
    else
    {
        // non cached write

        if (ByteOffset.QuadPart > OldFileSize.QuadPart)
        {
            CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
        }

        Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
        if (!NT_SUCCESS(Status))
        {
            goto ByeBye;
        }

        Status = VfatWriteFileData(IrpContext, Length, ByteOffset);
        if (NT_SUCCESS(Status))
        {
            IrpContext->Irp->IoStatus.Information = Length;
        }
    }

    if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
        !(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)))
    {
        if(!(*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY))
        {
            LARGE_INTEGER SystemTime;
            // set dates and times
            KeQuerySystemTime (&SystemTime);
            if (Fcb->Flags & FCB_IS_FATX_ENTRY)
            {
                FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
                                           &SystemTime, &Fcb->entry.FatX.UpdateDate,
                                           &Fcb->entry.FatX.UpdateTime);
                Fcb->entry.FatX.AccessDate = Fcb->entry.FatX.UpdateDate;
                Fcb->entry.FatX.AccessTime = Fcb->entry.FatX.UpdateTime;
            }
            else
            {
                FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
                                           &SystemTime, &Fcb->entry.Fat.UpdateDate,
                                           &Fcb->entry.Fat.UpdateTime);
                Fcb->entry.Fat.AccessDate = Fcb->entry.Fat.UpdateDate;
            }
            /* set date and times to dirty */
            Fcb->Flags |= FCB_IS_DIRTY;
        }
    }

ByeBye:
    if (Resource)
    {
        ExReleaseResourceLite(Resource);
    }

    if (Status == STATUS_PENDING)
    {
        Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
        if (NT_SUCCESS(Status))
        {
            Status = VfatQueueRequest(IrpContext);
        }
        else
        {
            IrpContext->Irp->IoStatus.Status = Status;
            IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
            VfatFreeIrpContext(IrpContext);
        }
    }
    else
    {
        IrpContext->Irp->IoStatus.Status = Status;
        if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
            !(IrpContext->Irp->Flags & IRP_PAGING_IO) && NT_SUCCESS(Status))
        {
            IrpContext->FileObject->CurrentByteOffset.QuadPart =
                ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
        }

        IoCompleteRequest(IrpContext->Irp,
                          (CCHAR)(NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT));
        VfatFreeIrpContext(IrpContext);
    }
    DPRINT("%x\n", Status);
    return Status;
}