IoReadPartitionTable函数是读取磁盘分区表数据。它的实现代码如下:
#001 NTSTATUS
#002 FASTCALL
#003 IoReadPartitionTable(IN PDEVICE_OBJECT DeviceObject,
#004 IN ULONG SectorSize,
#005 IN BOOLEAN ReturnRecognizedPartitions,
#006 IN OUT PDRIVE_LAYOUT_INFORMATION *PartitionBuffer)
#007 {
在这里调用硬件抽象层的函数,其实它是调用reactos/ntoskrnl/fstub/disksup.c里的函数。
#008 return HALDISPATCH->HalIoReadPartitionTable(DeviceObject,
#009 SectorSize,
#010 ReturnRecognizedPartitions,
#011 PartitionBuffer);
#012 }
下面来分析函数HalIoReadPartitionTable,它就是函数xHalIoReadPartitionTable的调用,上面使用调用表的原因,就是方便兼容不同的系统调用。具体实现代码如下:
#001 NTSTATUS
#002 FASTCALL
#003 xHalIoReadPartitionTable(IN PDEVICE_OBJECT DeviceObject,
#006 IN OUT PDRIVE_LAYOUT_INFORMATION *PartitionBuffer)
#007 {
#008 KEVENT Event;
#009 IO_STATUS_BLOCK IoStatusBlock;
#010 PIRP Irp;
#011 PPARTITION_DESCRIPTOR PartitionDescriptor;
#012 CCHAR Entry;
#013 NTSTATUS Status;
#014 PPARTITION_INFORMATION PartitionInfo;
#015 PUCHAR Buffer = NULL;
#016 ULONG BufferSize = 2048,InputSize;
#017 PDRIVE_LAYOUT_INFORMATION DriveLayoutInfo = NULL;
#018 LONG j = -1,i = -1,k;
#019 DISK_GEOMETRY DiskGeometry;
#020 LONGLONG EndSector,MaxSector,StartOffset;
#021 ULONGLONG MaxOffset;
#022 LARGE_INTEGER Offset,VolumeOffset;
#023 BOOLEAN IsPrimary = TRUE,IsEzDrive = FALSE,MbrFound = FALSE;
#024 BOOLEAN IsValid,IsEmpty = TRUE;
#025 PVOID MbrBuffer;
#026 PIO_STACK_LOCATION IoStackLocation;
#027 PBOOT_SECTOR_INFO BootSectorInfo = (PBOOT_SECTOR_INFO)Buffer;
#028 UCHAR PartitionType;
#029 LARGE_INTEGER HiddenSectors64;
#030 VolumeOffset.QuadPart = Offset.QuadPart = 0;
#031 PAGED_CODE();
#032
分配保存分区的内存区。
#033 /* Allocate the buffer */
#034 *PartitionBuffer = ExAllocatePoolWithTag(NonPagedPool,
#035 BufferSize,
#036 TAG_FILE_SYSTEM);
#037 if (!(*PartitionBuffer)) return STATUS_INSUFFICIENT_RESOURCES;
#038
输入的大小。
#039 /* Normalize the buffer size */
#040 InputSize = max(512,SectorSize);
#041
检查是否EZ的驱动设备。
#042 /* Check for EZ Drive */
#043 HalExamineMBR(DeviceObject,InputSize,0x55,&MbrBuffer);
#044 if (MbrBuffer)
#045 {
#046 /* EZ Drive found,bias the offset */
#047 IsEzDrive = TRUE;
#048 ExFreePool(MbrBuffer);
#049 Offset.QuadPart = 512;
#050 }
#051
#052 /* Get drive geometry */
#053 Status = HalpGetFullGeometry(DeviceObject,&DiskGeometry,&MaxOffset);
#054 if (!NT_SUCCESS(Status))
#055 {
#056 ExFreePoolWithTag(*PartitionBuffer,TAG_FILE_SYSTEM);
#057 *PartitionBuffer = NULL;
#058 return Status;
#059 }
#060
设置最大扇区数。
#061 /* Get the end and maximum sector */
#062 EndSector = MaxOffset;
#063 MaxSector = MaxOffset << 1;
#064 DPRINT("FSTUB: MaxOffset = %#I64x,MaxSector = %#I64x/n",
#065 MaxOffset,MaxSector);
#066
分配一页大小的缓冲区。
#067 /* Allocate our buffer */
#068 Buffer = ExAllocatePoolWithTag(NonPagedPool,PAGE_SIZE,TAG_FILE_SYSTEM);
#069 if (!Buffer)
#070 {
#071 /* Fail,free the input buffer */
#072 ExFreePoolWithTag(*PartitionBuffer,TAG_FILE_SYSTEM);
#073 *PartitionBuffer = NULL;
#074 return STATUS_INSUFFICIENT_RESOURCES;
#075 }
#076
开始分区循环处理。
#077 /* Start partition loop */
#078 do
#079 {
假定分区是无效的。
#080 /* Assume the partition is valid */
#081 IsValid = TRUE;
#082
初始化通知事件。
#083 /* Initialize the event */
#084 KeInitializeEvent(&Event,NotificationEvent,FALSE);
#085
清空缓冲区,然后创建一个读取MBR的IRP包。
#086 /* Clear the buffer and build the IRP */
#087 RtlZeroMemory(Buffer,InputSize);
#088 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
#089 DeviceObject,
#090 Buffer,
#091 InputSize,
#092 &Offset,
#093 &Event,
#094 &IoStatusBlock);
#095 if (!Irp)
#096 {
#097 /* Failed */
#098 Status = STATUS_INSUFFICIENT_RESOURCES;
#099 break;
#100 }
#101
获取IO的栈位置和设置不进行卷检验。
#102 /* Make sure to disable volume verification */
#103 IoStackLocation = IoGetNextIrpStackLocation(Irp);
#104 IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
#105
把这个IRP发送给低层的驱动程序进行处理。
#106 /* Call the driver */
#107 Status = IoCallDriver(DeviceObject,Irp);
#108 if (Status == STATUS_PENDING)
#109 {
如果这个IRP状态在阻塞中,说明这个IRP需要进入等待状态。
#110 /* Wait for completion */
#111 KeWaitForSingleObject(&Event,Executive,KernelMode,FALSE,NULL);
#112 Status = IoStatusBlock.Status;
#113 }
#114
#115 /* Normalize status code and check for failure */
#116 if (Status == STATUS_NO_DATA_DETECTED) Status = STATUS_SUCCESS;
如果状态不成功就终止分区查找。
#117 if (!NT_SUCCESS(Status)) break;
#118
如果是EZ的驱动设备,就设置为0开始。
#119 /* If we biased for EZ-Drive,unbias now */
#120 if (IsEzDrive && (Offset.QuadPart == 512)) Offset.QuadPart = 0;
#121
检查这个扇区标志是否为MBR的标志。
#122 /* Make sure this is a valid MBR */
#123 if (((PUSHORT)Buffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
#124 {
#125 /* It's not,fail */
#126 DPRINT1("FSTUB: (IoReadPartitionTable) No 0xaa55 found in "
#127 "partition table %d/n",j + 1);
#128 break;
#129 }
#130
这里确认这个是MBR扇区了。
#131 /* At this point we have a valid MBR */
#132 MbrFound = TRUE;
#133
#134 /* Check if we weren't given an offset */
#135 if (!Offset.QuadPart)
#136 {
获取分区的标志。
#137 /* Then read the signature off the disk */
#138 (*PartitionBuffer)->Signature = ((PULONG)Buffer)
#139 [PARTITION_TABLE_OFFSET / 2 - 1];
#140 }
#141
获取磁盘分区描述列表。
#142 /* Get the partition descriptor array */
#143 PartitionDescriptor = (PPARTITION_DESCRIPTOR)
#144 &(((PUSHORT)Buffer)[PARTITION_TABLE_OFFSET]);
#145
获取这个分区的类型。
#146 /* Get the partition type */
#147 PartitionType = PartitionDescriptor->PartitionType;
#148
下一个分区计数。
#149 /* Start looping partitions */
#150 j++;
#151 DPRINT("FSTUB: Partition Table %d:/n",j);
遍历四个分区表。
#152 for (Entry = 1,k = 0; Entry <= 4; Entry++,PartitionDescriptor++)
#153 {
获取分区类型。
#154 /* Get the partition type */
#155 PartitionType = PartitionDescriptor->PartitionType;
#156
#157 /* Print debug messages */
#158 DPRINT("Partition Entry %d,%d: type %#x %s/n",
#159 j,
#160 Entry,
#161 PartitionType,
#162 (PartitionDescriptor->ActiveFlag) ? "Active" : "");
#163 DPRINT("/tOffset %#08lx for %#08lx Sectors/n",
#164 GET_STARTING_SECTOR(PartitionDescriptor),
#165 GET_PARTITION_LENGTH(PartitionDescriptor));
#166
检查分区表是否有效。
#167 /* Make sure that the partition is valid,unless it's the first */
#168 if (!(HalpIsValidPartitionEntry(PartitionDescriptor,
#169 MaxOffset,
#170 MaxSector)) && !(j))
#171 {
#172 /* It's invalid,so fail */
#173 IsValid = FALSE;
#174 break;
#175 }
#176
检查否包括的分区类型。
#177 /* Check if it's a container */
#178 if (IsContainerPartition(PartitionType))
#179 {
#180 /* Increase the count of containers */
#181 if (++k != 1)
#182 {
#183 /* More then one table is invalid */
#184 DPRINT1("FSTUB: Multiple container partitions found in "
#185 "partition table %d/n - table is invalid/n",
#186 j);
#187 IsValid = FALSE;
#188 break;
#189 }
#190 }
#191
检查这个分区表是否为空。
#192 /* Check if the partition is supposedly empty */
#193 if (IsEmpty)
#194 {
#195 /* But check if it actually has a start and/or length */
#196 if ((GET_STARTING_SECTOR(PartitionDescriptor)) ||
#197 (GET_PARTITION_LENGTH(PartitionDescriptor)))
#198 {
#199 /* So then it's not really empty */
#200 IsEmpty = FALSE;
#201 }
#202 }
#203
如果调用这个函数只想读取已经声明类型的扇区,但有不认识的就直接返回。
#204 /* Check if the caller wanted only recognized partitions */
#205 if (ReturnRecognizedPartitions)
#206 {
#207 /* Then check if this one is unused,or a container */
#208 if ((PartitionType == PARTITION_ENTRY_UNUSED) ||
#209 IsContainerPartition(PartitionType))
#210 {
#211 /* Skip it,since the caller doesn't want it */
#212 continue;
#213 }
#214 }
#215
#216 /* Increase the structure count and check if they can fit */
检查内存里是否有下一个分区存在。
#217 if ((sizeof(DRIVE_LAYOUT_INFORMATION) +
#218 (++i * sizeof(PARTITION_INFORMATION))) >
#219 BufferSize)
#220 {
#221 /* Allocate a new buffer that's twice as big */
#222 DriveLayoutInfo = ExAllocatePoolWithTag(NonPagedPool,
#223 BufferSize << 1,
#224 TAG_FILE_SYSTEM);
#225 if (!DriveLayoutInfo)
#226 {
#227 /* Out of memory,unto this extra structure */
#228 --i;
#229 Status = STATUS_INSUFFICIENT_RESOURCES;
#230 break;
#231 }
#232
把旧缓冲区的内容移到设备信息里。
#233 /* Copy the contents of the old buffer */
#234 RtlMoveMemory(DriveLayoutInfo,
#235 *PartitionBuffer,
#236 BufferSize);
#237
#238 /* Free the old buffer and set this one as the new one */
#239 ExFreePoolWithTag(*PartitionBuffer,TAG_FILE_SYSTEM);
#240 *PartitionBuffer = DriveLayoutInfo;
#241
#242 /* Double the size */
#243 BufferSize <<= 1;
#244 }
#245
#246 /* Now get the current structure being filled and initialize it */
初始化当前分区属性。
#247 PartitionInfo = &(*PartitionBuffer)->PartitionEntry[i];
#248 PartitionInfo->PartitionType = PartitionType;
#249 PartitionInfo->RewritePartition = FALSE;
#250
#251 /* Check if we're dealing with a partition that's in use */
判断分区是否已经使用。
#252 if (PartitionType != PARTITION_ENTRY_UNUSED)
#253 {
#254 /* Check if it's bootable */
#255 PartitionInfo->BootIndicator = PartitionDescriptor->
#256 ActiveFlag & 0x80 ?
#257 TRUE : FALSE;
#258
#259 /* Check if its' a container */
#260 if (IsContainerPartition(PartitionType))
#261 {
#262 /* Then don't recognize it and use the volume offset */
#263 PartitionInfo->RecognizedPartition = FALSE;
#264 StartOffset = VolumeOffset.QuadPart;
#265 }
#266 else
#267 {
#268 /* Then recognize it and use the partition offset */
#269 PartitionInfo->RecognizedPartition = TRUE;
#270 StartOffset = Offset.QuadPart;
#271 }
#272
#273 /* Get the starting offset */
#274 PartitionInfo->StartingOffset.QuadPart =
#275 StartOffset +
#276 UInt32x32To64(GET_STARTING_SECTOR(PartitionDescriptor),
#277 SectorSize);
#278
#279 /* Calculate the number of hidden sectors */
#280 HiddenSectors64.QuadPart = (PartitionInfo->
#281 StartingOffset.QuadPart -
#282 StartOffset) /
#283 SectorSize;
#284 PartitionInfo->HiddenSectors = HiddenSectors64.LowPart;
#285
#286 /* Get the partition length */
#287 PartitionInfo->PartitionLength.QuadPart =
#288 UInt32x32To64(GET_PARTITION_LENGTH(PartitionDescriptor),
#289 SectorSize);
#290
#291 /* FIXME: REACTOS HACK */
#292 PartitionInfo->PartitionNumber = i + 1;
#293 }
#294 else
#295 {
#296 /* Otherwise,clear all the relevant fields */
#297 PartitionInfo->BootIndicator = FALSE;
#298 PartitionInfo->RecognizedPartition = FALSE;
#299 PartitionInfo->StartingOffset.QuadPart = 0;
#300 PartitionInfo->PartitionLength.QuadPart = 0;
#301 PartitionInfo->HiddenSectors = 0;
#302
#303 /* FIXME: REACTOS HACK */
#304 PartitionInfo->PartitionNumber = 0;
#305 }
#306 }
#307
如果返回状态是错误的,就返回出错。
#308 /* Finish debug log,and check for failure */
#309 DPRINT("/n");
#310 if (!NT_SUCCESS(Status)) break;
#311
#312 /* Also check if we hit an invalid entry here */
#313 if (!IsValid)
#314 {
#315 /* We did,so break out of the loop minus one entry */
#316 j--;
#317 break;
#318 }
#319
#320 /* Reset the offset */
#321 Offset.QuadPart = 0;
#322
#323 /* Go back to the descriptor array and loop it */
#324 PartitionDescriptor = (PPARTITION_DESCRIPTOR)
#325 &(((PUSHORT)Buffer)[PARTITION_TABLE_OFFSET]);
#326 for (Entry = 1; Entry <= 4; Entry++,PartitionDescriptor++)
#327 {
#328 /* Check if this is a container partition,since we skipped them */
#329 if (IsContainerPartition(PartitionType))
#330 {
#331 /* Get its offset */
#332 Offset.QuadPart = VolumeOffset.QuadPart +
#333 UInt32x32To64(
#334 GET_STARTING_SECTOR(PartitionDescriptor),
#335 SectorSize);
#336
#337 /* If this is a primary partition,this is the volume offset */
#338 if (IsPrimary) VolumeOffset = Offset;
#339
#340 /* Also update the maximum sector */
#341 MaxSector = GET_PARTITION_LENGTH(PartitionDescriptor);
#342 DPRINT1("FSTUB: MaxSector now = %#08lx/n",MaxSector);
#343 break;
#344 }
#345 }
#346
#347 /* Loop the next partitions,which are not primary anymore */
#348 IsPrimary = FALSE;
#349 } while (Offset.HighPart | Offset.LowPart);
#350
#351 /* Check if this is a removable device that's probably a super-floppy */
检查是否可移动的设备。
#352 if ((DiskGeometry.MediaType == RemovableMedia) &&
#353 !(j) &&
#354 (MbrFound) &&
#355 (IsEmpty))
#356 {
#357 /* Read the jump bytes to detect super-floppy */
#358 if ((BootSectorInfo->JumpByte[0] == 0xeb) ||
#359 (BootSectorInfo->JumpByte[0] == 0xe9))
#360 {
#361 /* Super floppes don't have typical MBRs,so skip them */
#362 DPRINT1("FSTUB: Jump byte %#x found along with empty partition "
#363 "table - disk is a super floppy and has no valid MBR/n",
#364 BootSectorInfo->JumpByte);
#365 j = -1;
#366 }
#367 }
#368
检查是否没有找到磁盘分区。
#369 /* Check if we're still at partition -1 */
#370 if (j == -1)
#371 {
#372 /* The likely cause is the super floppy detection above */
#373 if ((MbrFound) || (DiskGeometry.MediaType == RemovableMedia))
#374 {
#375 /* Print out debugging information */
#376 DPRINT1("FSTUB: Drive %#p has no valid MBR. Make it into a "
#377 "super-floppy/n",
#378 DeviceObject);
#379 DPRINT1("FSTUB: Drive has %#08lx sectors and is %#016I64x "
#380 "bytes large/n",
#381 EndSector,EndSector * DiskGeometry.BytesPerSector);
#382
#383 /* We should at least have some sectors */
#384 if (EndSector > 0)
#385 {
#386 /* Get the entry we'll use */
#387 PartitionInfo = &(*PartitionBuffer)->PartitionEntry[0];
#388
#389 /* Fill it out with data for a super-floppy */
#390 PartitionInfo->RewritePartition = FALSE;
#391 PartitionInfo->RecognizedPartition = TRUE;
#392 PartitionInfo->PartitionType = PARTITION_FAT_16;
#393 PartitionInfo->BootIndicator = FALSE;
#394 PartitionInfo->HiddenSectors = 0;
#395 PartitionInfo->StartingOffset.QuadPart = 0;
#396 PartitionInfo->PartitionLength.QuadPart = (EndSector *
#397 DiskGeometry.
#398 BytesPerSector);
#399
#400 /* FIXME: REACTOS HACK */
#401 PartitionInfo->PartitionNumber = 0;
#402
#403 /* Set the signature and set the count back to 0 */
#404 (*PartitionBuffer)->Signature = 1;
#405 i = 0;
#406 }
#407 }
#408 else
#409 {
#410 /* Otherwise,this isn't a super floppy,so set an invalid count */
#411 i = -1;
#412 }
#413 }
#414
设置当前找到的分区数。
#415 /* Set the partition count */
#416 (*PartitionBuffer)->PartitionCount = ++i;
#417
#418 /* If we have no count,delete the signature */
#419 if (!i) (*PartitionBuffer)->Signature = 0;
#420
释放前面分配的内存。
#421 /* Free the buffer and check for success */
#422 if (Buffer) ExFreePoolWithTag(Buffer,TAG_FILE_SYSTEM);
#423 if (!NT_SUCCESS(Status)) ExFreePoolWithTag(*PartitionBuffer,TAG_FILE_SYSTEM);
#424
#425 /* Return status */
#426 return Status;
#427}