PNTFS_FCB NtfsMakeRootFCB(PNTFS_VCB Vcb) { PNTFS_FCB Fcb; PFILE_RECORD_HEADER MftRecord; PFILENAME_ATTRIBUTE FileName; MftRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS); if (MftRecord == NULL) { return NULL; } if (!NT_SUCCESS(ReadFileRecord(Vcb, NTFS_FILE_ROOT, MftRecord))) { ExFreePoolWithTag(MftRecord, TAG_NTFS); return NULL; } FileName = GetFileNameFromRecord(Vcb, MftRecord, NTFS_FILE_NAME_WIN32); if (!FileName) { ExFreePoolWithTag(MftRecord, TAG_NTFS); return NULL; } Fcb = NtfsCreateFCB(L"\\", NULL, Vcb); if (!Fcb) { ExFreePoolWithTag(MftRecord, TAG_NTFS); return NULL; } memcpy(&Fcb->Entry, FileName, FIELD_OFFSET(FILENAME_ATTRIBUTE, NameLength)); Fcb->Entry.NameType = FileName->NameType; Fcb->Entry.NameLength = 0; Fcb->Entry.Name[0] = UNICODE_NULL; Fcb->RefCount = 1; Fcb->DirIndex = 0; Fcb->RFCB.FileSize.QuadPart = FileName->DataSize; Fcb->RFCB.ValidDataLength.QuadPart = FileName->DataSize; Fcb->RFCB.AllocationSize.QuadPart = FileName->AllocatedSize; Fcb->MFTIndex = NTFS_FILE_ROOT; Fcb->LinkCount = MftRecord->LinkCount; NtfsFCBInitializeCache(Vcb, Fcb); NtfsAddFCBToTable(Vcb, Fcb); NtfsGrabFCB(Vcb, Fcb); ExFreePoolWithTag(MftRecord, TAG_NTFS); return Fcb; }
static NTSTATUS NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, PDEVICE_EXTENSION DeviceExt) { DISK_GEOMETRY DiskGeometry; PFILE_RECORD_HEADER MftRecord; PFILE_RECORD_HEADER VolumeRecord; PVOLINFO_ATTRIBUTE VolumeInfo; PBOOT_SECTOR BootSector; PATTRIBUTE Attribute; ULONG Size; PNTFS_INFO NtfsInfo = &DeviceExt->NtfsInfo; NTSTATUS Status; DPRINT("NtfsGetVolumeData() called\n"); Size = sizeof(DISK_GEOMETRY); Status = NtfsDeviceIoControl(DeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DiskGeometry, &Size, TRUE); if (!NT_SUCCESS(Status)) { DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status); return Status; } DPRINT("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector); BootSector = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_NTFS); if (BootSector == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Status = NtfsReadSectors(DeviceObject, 0, /* Partition boot sector */ 1, DiskGeometry.BytesPerSector, (PVOID)BootSector, TRUE); if (!NT_SUCCESS(Status)) { ExFreePool(BootSector); return Status; } /* Read data from the bootsector */ NtfsInfo->BytesPerSector = BootSector->BPB.BytesPerSector; NtfsInfo->SectorsPerCluster = BootSector->BPB.SectorsPerCluster; NtfsInfo->BytesPerCluster = BootSector->BPB.BytesPerSector * BootSector->BPB.SectorsPerCluster; NtfsInfo->SectorCount = BootSector->EBPB.SectorCount; NtfsInfo->MftStart.QuadPart = BootSector->EBPB.MftLocation; NtfsInfo->MftMirrStart.QuadPart = BootSector->EBPB.MftMirrLocation; NtfsInfo->SerialNumber = BootSector->EBPB.SerialNumber; if (BootSector->EBPB.ClustersPerMftRecord > 0) NtfsInfo->BytesPerFileRecord = BootSector->EBPB.ClustersPerMftRecord * NtfsInfo->BytesPerCluster; else NtfsInfo->BytesPerFileRecord = 1 << (-BootSector->EBPB.ClustersPerMftRecord); DPRINT("Boot sector information:\n"); DPRINT(" BytesPerSector: %hu\n", BootSector->BPB.BytesPerSector); DPRINT(" SectorsPerCluster: %hu\n", BootSector->BPB.SectorsPerCluster); DPRINT(" SectorCount: %I64u\n", BootSector->EBPB.SectorCount); DPRINT(" MftStart: %I64u\n", BootSector->EBPB.MftLocation); DPRINT(" MftMirrStart: %I64u\n", BootSector->EBPB.MftMirrLocation); DPRINT(" ClustersPerMftRecord: %lx\n", BootSector->EBPB.ClustersPerMftRecord); DPRINT(" ClustersPerIndexRecord: %lx\n", BootSector->EBPB.ClustersPerIndexRecord); DPRINT(" SerialNumber: %I64x\n", BootSector->EBPB.SerialNumber); ExFreePool(BootSector); MftRecord = ExAllocatePoolWithTag(NonPagedPool, NtfsInfo->BytesPerFileRecord, TAG_NTFS); if (MftRecord == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Status = NtfsReadSectors(DeviceObject, NtfsInfo->MftStart.u.LowPart * NtfsInfo->SectorsPerCluster, NtfsInfo->BytesPerFileRecord / NtfsInfo->BytesPerSector, NtfsInfo->BytesPerSector, (PVOID)MftRecord, TRUE); if (!NT_SUCCESS(Status)) { ExFreePool(MftRecord); return Status; } VolumeRecord = ExAllocatePoolWithTag(NonPagedPool, NtfsInfo->BytesPerFileRecord, TAG_NTFS); if (VolumeRecord == NULL) { ExFreePool(MftRecord); return STATUS_INSUFFICIENT_RESOURCES; } /* Read Volume File (MFT index 3) */ DeviceExt->StorageDevice = DeviceObject; Status = ReadFileRecord(DeviceExt, 3, VolumeRecord, MftRecord); if (!NT_SUCCESS(Status)) { ExFreePool(MftRecord); return Status; } /* Enumerate attributes */ NtfsDumpFileAttributes (MftRecord); /* Enumerate attributes */ NtfsDumpFileAttributes (VolumeRecord); /* Get volume name */ Attribute = FindAttribute (VolumeRecord, AttributeVolumeName, NULL); DPRINT("Attribute %p\n", Attribute); if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0) { DPRINT("Data length %lu\n", AttributeDataLength (Attribute)); NtfsInfo->VolumeLabelLength = min (((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH); RtlCopyMemory(NtfsInfo->VolumeLabel, (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset), NtfsInfo->VolumeLabelLength); } else { NtfsInfo->VolumeLabelLength = 0; } /* Get volume information */ Attribute = FindAttribute (VolumeRecord, AttributeVolumeInformation, NULL); DPRINT("Attribute %p\n", Attribute); if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0) { DPRINT("Data length %lu\n", AttributeDataLength (Attribute)); VolumeInfo = (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset); NtfsInfo->MajorVersion = VolumeInfo->MajorVersion; NtfsInfo->MinorVersion = VolumeInfo->MinorVersion; NtfsInfo->Flags = VolumeInfo->Flags; } ExFreePool(MftRecord); ExFreePool(VolumeRecord); return Status; }
ULONGLONG NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt) { NTSTATUS Status; PFILE_RECORD_HEADER BitmapRecord; PNTFS_ATTR_CONTEXT DataContext; ULONGLONG BitmapDataSize; PCHAR BitmapData; ULONGLONG FreeClusters = 0; ULONG Read = 0; RTL_BITMAP Bitmap; DPRINT1("NtfsGetFreeClusters(%p)\n", DeviceExt); BitmapRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); if (BitmapRecord == NULL) { return 0; } Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return 0; } Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return 0; } BitmapDataSize = AttributeDataLength(DataContext->pRecord); ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount); BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS); if (BitmapData == NULL) { ReleaseAttributeContext(DataContext); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return 0; } /* FIXME: Totally underoptimized! */ for (; Read < BitmapDataSize; Read += DeviceExt->NtfsInfo.BytesPerSector) { ReadAttribute(DeviceExt, DataContext, Read, (PCHAR)((ULONG_PTR)BitmapData + Read), DeviceExt->NtfsInfo.BytesPerSector); } ReleaseAttributeContext(DataContext); DPRINT1("Total clusters: %I64x\n", DeviceExt->NtfsInfo.ClusterCount); DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8); DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) - DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster * DeviceExt->NtfsInfo.BytesPerSector); RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount); FreeClusters = RtlNumberOfClearBits(&Bitmap); ExFreePoolWithTag(BitmapData, TAG_NTFS); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return FreeClusters; }
/** * NtfsAllocateClusters * Allocates a run of clusters. The run allocated might be smaller than DesiredClusters. */ NTSTATUS NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt, ULONG FirstDesiredCluster, ULONG DesiredClusters, PULONG FirstAssignedCluster, PULONG AssignedClusters) { NTSTATUS Status; PFILE_RECORD_HEADER BitmapRecord; PNTFS_ATTR_CONTEXT DataContext; ULONGLONG BitmapDataSize; PUCHAR BitmapData; ULONGLONG FreeClusters = 0; RTL_BITMAP Bitmap; ULONG AssignedRun; ULONG LengthWritten; DPRINT1("NtfsAllocateClusters(%p, %lu, %lu, %p, %p)\n", DeviceExt, FirstDesiredCluster, DesiredClusters, FirstAssignedCluster, AssignedClusters); BitmapRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); if (BitmapRecord == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return Status; } Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return Status; } BitmapDataSize = AttributeDataLength(DataContext->pRecord); BitmapDataSize = min(BitmapDataSize, 0xffffffff); ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount); BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS); if (BitmapData == NULL) { ReleaseAttributeContext(DataContext); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return STATUS_INSUFFICIENT_RESOURCES; } DPRINT1("Total clusters: %I64x\n", DeviceExt->NtfsInfo.ClusterCount); DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8); DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) - DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster * DeviceExt->NtfsInfo.BytesPerSector); ReadAttribute(DeviceExt, DataContext, 0, (PCHAR)BitmapData, (ULONG)BitmapDataSize); RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount); FreeClusters = RtlNumberOfClearBits(&Bitmap); if (FreeClusters < DesiredClusters) { ReleaseAttributeContext(DataContext); ExFreePoolWithTag(BitmapData, TAG_NTFS); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return STATUS_DISK_FULL; } // TODO: Observe MFT reservation zone // Can we get one contiguous run? AssignedRun = RtlFindClearBitsAndSet(&Bitmap, DesiredClusters, FirstDesiredCluster); if (AssignedRun != 0xFFFFFFFF) { *FirstAssignedCluster = AssignedRun; *AssignedClusters = DesiredClusters; } else { // we can't get one contiguous run *AssignedClusters = RtlFindNextForwardRunClear(&Bitmap, FirstDesiredCluster, FirstAssignedCluster); if (*AssignedClusters == 0) { // we couldn't find any runs starting at DesiredFirstCluster *AssignedClusters = RtlFindLongestRunClear(&Bitmap, FirstAssignedCluster); } } Status = WriteAttribute(DeviceExt, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten, BitmapRecord); ReleaseAttributeContext(DataContext); ExFreePoolWithTag(BitmapData, TAG_NTFS); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return Status; }
/* * FUNCTION: Reads data from a file */ static NTSTATUS NtfsReadFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject, PUCHAR Buffer, ULONG Length, ULONG ReadOffset, ULONG IrpFlags, PULONG LengthRead) { NTSTATUS Status = STATUS_SUCCESS; PNTFS_FCB Fcb; PFILE_RECORD_HEADER FileRecord; PNTFS_ATTR_CONTEXT DataContext; ULONG RealLength; ULONG RealReadOffset; ULONG RealLengthRead; ULONG ToRead; BOOLEAN AllocatedBuffer = FALSE; PCHAR ReadBuffer = (PCHAR)Buffer; DPRINT1("NtfsReadFile(%p, %p, %p, %u, %u, %x, %p)\n", DeviceExt, FileObject, Buffer, Length, ReadOffset, IrpFlags, LengthRead); *LengthRead = 0; if (Length == 0) { DPRINT1("Null read!\n"); return STATUS_SUCCESS; } Fcb = (PNTFS_FCB)FileObject->FsContext; if (ReadOffset >= Fcb->Entry.AllocatedSize) { DPRINT1("Reading beyond file end!\n"); return STATUS_END_OF_FILE; } ToRead = Length; if (ReadOffset + Length > Fcb->Entry.AllocatedSize) ToRead = Fcb->Entry.AllocatedSize - ReadOffset; RealReadOffset = ReadOffset; RealLength = ToRead; if ((ReadOffset % DeviceExt->NtfsInfo.BytesPerSector) != 0 || (ToRead % DeviceExt->NtfsInfo.BytesPerSector) != 0) { RealReadOffset = ROUND_DOWN(ReadOffset, DeviceExt->NtfsInfo.BytesPerSector); RealLength = ROUND_UP(ToRead, DeviceExt->NtfsInfo.BytesPerSector); ReadBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength + DeviceExt->NtfsInfo.BytesPerSector, TAG_NTFS); if (ReadBuffer == NULL) { DPRINT1("Not enough memory!\n"); return STATUS_INSUFFICIENT_RESOURCES; } AllocatedBuffer = TRUE; } FileRecord = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS); if (FileRecord == NULL) { DPRINT1("Not enough memory!\n"); return STATUS_INSUFFICIENT_RESOURCES; } Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord); if (!NT_SUCCESS(Status)) { DPRINT1("Can't find record!\n"); ExFreePoolWithTag(FileRecord, TAG_NTFS); return Status; } Status = FindAttribute(DeviceExt, FileRecord, AttributeData, L"", 0, &DataContext); if (!NT_SUCCESS(Status)) { DPRINT1("No data associated with file!\n"); ExFreePoolWithTag(FileRecord, TAG_NTFS); return Status; } DPRINT1("Effective read: %lu at %lu\n", RealLength, RealReadOffset); RealLengthRead = ReadAttribute(DeviceExt, DataContext, RealReadOffset, (PCHAR)ReadBuffer, RealLength); if (RealLengthRead != RealLength) { DPRINT1("Read failure!\n"); ReleaseAttributeContext(DataContext); ExFreePoolWithTag(FileRecord, TAG_NTFS); if (AllocatedBuffer) { ExFreePoolWithTag(ReadBuffer, TAG_NTFS); } return Status; } ReleaseAttributeContext(DataContext); ExFreePoolWithTag(FileRecord, TAG_NTFS); *LengthRead = ToRead; DPRINT1("%lu got read\n", *LengthRead); if (AllocatedBuffer) { RtlCopyMemory(Buffer, ReadBuffer + (ReadOffset - RealReadOffset), ToRead); } if (ToRead != Length) { RtlZeroMemory(Buffer + ToRead, Length - ToRead); } if (AllocatedBuffer) { ExFreePoolWithTag(ReadBuffer, TAG_NTFS); } return STATUS_SUCCESS; }