Sync with trunk head (r49139)
[reactos.git] / ntoskrnl / fstub / fstubex.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fstub/fstubex.c
5 * PURPOSE: Extended FSTUB Routines (not linked to HAL)
6 * PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 #define PARTITION_ENTRY_SIZE 128
18 #define TAG_FSTUB 'BtsF'
19
20 typedef struct _DISK_INFORMATION
21 {
22 PDEVICE_OBJECT DeviceObject;
23 ULONG SectorSize;
24 DISK_GEOMETRY_EX DiskGeometry;
25 PUSHORT Buffer;
26 ULONGLONG SectorCount;
27 } DISK_INFORMATION, *PDISK_INFORMATION;
28
29 /* Defines system type for MBR showing that a GPT is following */
30 #define EFI_PMBR_OSTYPE_EFI 0xEE
31
32 #define IS_VALID_DISK_INFO(Disk) \
33 (Disk) && \
34 (Disk->DeviceObject) && \
35 (Disk->SectorSize) && \
36 (Disk->Buffer) && \
37 (Disk->SectorCount)
38
39 VOID
40 NTAPI
41 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry,
42 IN ULONG PartitionNumber
43 );
44
45 NTSTATUS
46 NTAPI
47 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject,
48 OUT PDISK_GEOMETRY_EX Geometry
49 );
50
51 NTSTATUS
52 NTAPI
53 FstubReadSector(IN PDEVICE_OBJECT DeviceObject,
54 IN ULONG SectorSize,
55 IN ULONGLONG StartingSector OPTIONAL,
56 OUT PUSHORT Buffer
57 );
58
59 NTSTATUS
60 NTAPI
61 FstubAllocateDiskInformation(IN PDEVICE_OBJECT DeviceObject,
62 OUT PDISK_INFORMATION * DiskBuffer,
63 PDISK_GEOMETRY_EX DiskGeometry OPTIONAL)
64 {
65 NTSTATUS Status;
66 PDISK_INFORMATION DiskInformation;
67 PAGED_CODE();
68
69 ASSERT(DeviceObject);
70 ASSERT(DiskBuffer);
71
72 /* Allocate internal structure */
73 DiskInformation = ExAllocatePoolWithTag(NonPagedPool, sizeof(DISK_INFORMATION), TAG_FSTUB);
74 if (!DiskInformation)
75 {
76 return STATUS_INSUFFICIENT_RESOURCES;
77 }
78
79 /* If caller don't pass needed information, let's get them */
80 if (!DiskGeometry)
81 {
82 Status = FstubGetDiskGeometry(DeviceObject, &(DiskInformation->DiskGeometry));
83 if (!NT_SUCCESS(Status))
84 {
85 ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
86 return Status;
87 }
88 }
89 else
90 {
91 DiskInformation->DiskGeometry = *DiskGeometry;
92 }
93
94 /* Ensure read/received information are correct */
95 if (DiskInformation->DiskGeometry.Geometry.BytesPerSector == 0 ||
96 DiskInformation->DiskGeometry.DiskSize.QuadPart == 0)
97 {
98 ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
99 return STATUS_DEVICE_NOT_READY;
100 }
101
102 /* Store vital information as well */
103 DiskInformation->DeviceObject = DeviceObject;
104 DiskInformation->SectorSize = DiskInformation->DiskGeometry.Geometry.BytesPerSector;
105 DiskInformation->SectorCount = DiskInformation->DiskGeometry.DiskSize.QuadPart / DiskInformation->SectorSize;
106
107 /* Finally, allocate the buffer that will be used for different read */
108 DiskInformation->Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, DiskInformation->SectorSize, TAG_FSTUB);
109 if (!DiskInformation->Buffer)
110 {
111 ExFreePoolWithTag(DiskInformation, TAG_FSTUB);
112 return STATUS_INSUFFICIENT_RESOURCES;
113 }
114
115 /* Return allocated internal structure */
116 *DiskBuffer = DiskInformation;
117
118 return STATUS_SUCCESS;
119 }
120
121 PCHAR
122 NTAPI
123 FstubDbgGuidToString(IN PGUID Guid,
124 OUT PCHAR String)
125 {
126 sprintf(String,
127 "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
128 Guid->Data1,
129 Guid->Data2,
130 Guid->Data3,
131 Guid->Data4[0],
132 Guid->Data4[1],
133 Guid->Data4[2],
134 Guid->Data4[3],
135 Guid->Data4[4],
136 Guid->Data4[5],
137 Guid->Data4[6],
138 Guid->Data4[7]);
139
140 return String;
141 }
142
143 VOID
144 NTAPI
145 FstubDbgPrintDriveLayoutEx(IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout)
146 {
147 ULONG i;
148 CHAR Guid[38];
149 PAGED_CODE();
150
151 DPRINT1("FSTUB: DRIVE_LAYOUT_INFORMATION_EX: %p\n", DriveLayout);
152 switch (DriveLayout->PartitionStyle)
153 {
154 case PARTITION_STYLE_MBR:
155 if (DriveLayout->PartitionCount % 4 != 0)
156 {
157 DPRINT1("Warning: Partition count isn't a 4-factor: %ld!\n", DriveLayout->PartitionCount);
158 }
159
160 DPRINT1("Signature: %8.8x\n", DriveLayout->Mbr.Signature);
161 for (i = 0; i < DriveLayout->PartitionCount; i++)
162 {
163 FstubDbgPrintPartitionEx(DriveLayout->PartitionEntry, i);
164 }
165
166 break;
167 case PARTITION_STYLE_GPT:
168 FstubDbgGuidToString(&(DriveLayout->Gpt.DiskId), Guid);
169 DPRINT1("DiskId: %s\n", Guid);
170 DPRINT1("StartingUsableOffset: %I64x\n", DriveLayout->Gpt.StartingUsableOffset.QuadPart);
171 DPRINT1("UsableLength: %I64x\n", DriveLayout->Gpt.UsableLength.QuadPart);
172 DPRINT1("MaxPartitionCount: %ld\n", DriveLayout->Gpt.MaxPartitionCount);
173 for (i = 0; i < DriveLayout->PartitionCount; i++)
174 {
175 FstubDbgPrintPartitionEx(DriveLayout->PartitionEntry, i);
176 }
177
178 break;
179 default:
180 DPRINT1("Unsupported partition style: %ld\n", DriveLayout->PartitionStyle);
181 }
182 }
183
184 VOID
185 NTAPI
186 FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry,
187 IN ULONG PartitionNumber)
188 {
189 CHAR Guid[38];
190 PAGED_CODE();
191
192 DPRINT1("Printing partition %ld\n", PartitionNumber);
193
194 switch (PartitionEntry[PartitionNumber].PartitionStyle)
195 {
196 case PARTITION_STYLE_MBR:
197 DPRINT1(" StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
198 DPRINT1(" PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
199 DPRINT1(" RewritePartition: %d\n", PartitionEntry[PartitionNumber].RewritePartition);
200 DPRINT1(" PartitionType: %02x\n", PartitionEntry[PartitionNumber].Mbr.PartitionType);
201 DPRINT1(" BootIndicator: %d\n", PartitionEntry[PartitionNumber].Mbr.BootIndicator);
202 DPRINT1(" RecognizedPartition: %d\n", PartitionEntry[PartitionNumber].Mbr.RecognizedPartition);
203 DPRINT1(" HiddenSectors: %ld\n", PartitionEntry[PartitionNumber].Mbr.HiddenSectors);
204
205 break;
206 case PARTITION_STYLE_GPT:
207 DPRINT1(" StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
208 DPRINT1(" PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
209 DPRINT1(" RewritePartition: %d\n", PartitionEntry[PartitionNumber].RewritePartition);
210 FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionType), Guid);
211 DPRINT1(" PartitionType: %s\n", Guid);
212 FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionId), Guid);
213 DPRINT1(" PartitionId: %s\n", Guid);
214 DPRINT1(" Attributes: %16x\n", PartitionEntry[PartitionNumber].Gpt.Attributes);
215 DPRINT1(" Name: %ws\n", PartitionEntry[PartitionNumber].Gpt.Name);
216
217 break;
218 default:
219 DPRINT1(" Unsupported partition style: %ld\n", PartitionEntry[PartitionNumber].PartitionStyle);
220 }
221 }
222
223 NTSTATUS
224 NTAPI
225 FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk,
226 IN PARTITION_STYLE * PartitionStyle)
227 {
228 NTSTATUS Status;
229 PPARTITION_DESCRIPTOR PartitionDescriptor;
230 PAGED_CODE();
231
232 ASSERT(IS_VALID_DISK_INFO(Disk));
233 ASSERT(PartitionStyle);
234
235 /* Read disk first sector */
236 Status = FstubReadSector(Disk->DeviceObject,
237 Disk->SectorSize,
238 0,
239 Disk->Buffer);
240 if (!NT_SUCCESS(Status))
241 {
242 return Status;
243 }
244
245 /* Get the partition descriptor array */
246 PartitionDescriptor = (PPARTITION_DESCRIPTOR)
247 &(Disk->Buffer[PARTITION_TABLE_OFFSET]);
248 /* If we have not the 0xAA55 then it's raw partition */
249 if (Disk->Buffer[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
250 {
251 *PartitionStyle = PARTITION_STYLE_RAW;
252 }
253 /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
254 else if (PartitionDescriptor[0].PartitionType == EFI_PMBR_OSTYPE_EFI &&
255 PartitionDescriptor[1].PartitionType == 0 &&
256 PartitionDescriptor[2].PartitionType == 0 &&
257 PartitionDescriptor[3].PartitionType == 0)
258 {
259 *PartitionStyle = PARTITION_STYLE_GPT;
260 }
261 /* Otherwise, partition table is in MBR */
262 else
263 {
264 *PartitionStyle = PARTITION_STYLE_MBR;
265 }
266
267 return STATUS_SUCCESS;
268 }
269
270 VOID
271 NTAPI
272 FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer)
273 {
274 if (DiskBuffer)
275 {
276 if (DiskBuffer->Buffer)
277 {
278 ExFreePoolWithTag(DiskBuffer->Buffer, TAG_FSTUB);
279 }
280 ExFreePoolWithTag(DiskBuffer, TAG_FSTUB);
281 }
282 }
283
284 NTSTATUS
285 NTAPI
286 FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject,
287 OUT PDISK_GEOMETRY_EX Geometry)
288 {
289 PIRP Irp;
290 NTSTATUS Status;
291 PKEVENT Event = NULL;
292 PDISK_GEOMETRY_EX DiskGeometry = NULL;
293 PIO_STATUS_BLOCK IoStatusBlock = NULL;
294 PAGED_CODE();
295
296 ASSERT(DeviceObject);
297 ASSERT(Geometry);
298
299 /* Allocate needed components */
300 DiskGeometry = ExAllocatePoolWithTag(NonPagedPool, sizeof(DISK_GEOMETRY_EX), TAG_FSTUB);
301 if (!DiskGeometry)
302 {
303 Status = STATUS_INSUFFICIENT_RESOURCES;
304 goto Cleanup;
305 }
306
307 IoStatusBlock = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), TAG_FSTUB);
308 if (!IoStatusBlock)
309 {
310 Status = STATUS_INSUFFICIENT_RESOURCES;
311 goto Cleanup;
312 }
313
314 Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_FSTUB);
315 if (!Event)
316 {
317 Status = STATUS_INSUFFICIENT_RESOURCES;
318 goto Cleanup;
319 }
320 /* Initialize the waiting event */
321 KeInitializeEvent(Event, NotificationEvent, FALSE);
322
323 /* Build the request to get disk geometry */
324 Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
325 DeviceObject,
326 0,
327 0,
328 DiskGeometry,
329 sizeof(DISK_GEOMETRY_EX),
330 FALSE,
331 Event,
332 IoStatusBlock);
333 if (!Irp)
334 {
335 Status = STATUS_INSUFFICIENT_RESOURCES;
336 goto Cleanup;
337 }
338
339 /* Call the driver and wait for completion if needed */
340 Status = IoCallDriver(DeviceObject, Irp);
341 if (Status == STATUS_PENDING)
342 {
343 KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
344 Status = IoStatusBlock->Status;
345 }
346
347 /* In case of a success, return read data */
348 if (NT_SUCCESS(Status))
349 {
350 *Geometry = *DiskGeometry;
351 }
352
353 Cleanup:
354 if (DiskGeometry)
355 {
356 ExFreePoolWithTag(DiskGeometry, TAG_FSTUB);
357
358 if (NT_SUCCESS(Status))
359 {
360 ASSERT(Geometry->Geometry.BytesPerSector % PARTITION_ENTRY_SIZE == 0);
361 }
362 }
363
364 if (IoStatusBlock)
365 {
366 ExFreePoolWithTag(IoStatusBlock, TAG_FSTUB);
367 }
368
369 if (Event)
370 {
371 ExFreePoolWithTag(Event, TAG_FSTUB);
372 }
373
374 return Status;
375 }
376
377 NTSTATUS
378 NTAPI
379 FstubReadPartitionTableEFI(IN PDISK_INFORMATION Disk,
380 IN BOOLEAN ReadBackupTable,
381 OUT struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
382 {
383 UNIMPLEMENTED;
384 return STATUS_NOT_IMPLEMENTED;
385 }
386
387 NTSTATUS
388 NTAPI
389 FstubReadPartitionTableMBR(IN PDISK_INFORMATION Disk,
390 IN BOOLEAN ReturnRecognizedPartitions,
391 OUT struct _DRIVE_LAYOUT_INFORMATION_EX** ReturnedDriveLayout)
392 {
393 ULONG i;
394 NTSTATUS Status;
395 PDRIVE_LAYOUT_INFORMATION DriveLayout = NULL;
396 PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
397 PAGED_CODE();
398
399 ASSERT(IS_VALID_DISK_INFO(Disk));
400 ASSERT(ReturnedDriveLayout);
401
402 /* Zero output */
403 *ReturnedDriveLayout = NULL;
404
405 /* Read partition table the old way */
406 Status = IoReadPartitionTable(Disk->DeviceObject,
407 Disk->SectorSize,
408 ReturnRecognizedPartitions,
409 &DriveLayout);
410 if (!NT_SUCCESS(Status))
411 {
412 return Status;
413 }
414
415 /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
416 DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
417 FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
418 DriveLayout->PartitionCount * sizeof(PARTITION_INFORMATION_EX),
419 TAG_FSTUB);
420 if (!DriveLayoutEx)
421 {
422 /* Let's not leak memory as in Windows 2003 */
423 ExFreePool(DriveLayout);
424 return STATUS_INSUFFICIENT_RESOURCES;
425 }
426
427 /* Start converting the DRIVE_LAYOUT_INFORMATION structure */
428 DriveLayoutEx->PartitionStyle = PARTITION_STYLE_MBR;
429 DriveLayoutEx->PartitionCount = DriveLayout->PartitionCount;
430 DriveLayoutEx->Mbr.Signature = DriveLayout->Signature;
431
432 /* Convert each found partition */
433 for (i = 0; i < DriveLayout->PartitionCount; i++)
434 {
435 DriveLayoutEx->PartitionEntry[i].PartitionStyle = PARTITION_STYLE_MBR;
436 DriveLayoutEx->PartitionEntry[i].StartingOffset = DriveLayout->PartitionEntry[i].StartingOffset;
437 DriveLayoutEx->PartitionEntry[i].PartitionLength = DriveLayout->PartitionEntry[i].PartitionLength;
438 DriveLayoutEx->PartitionEntry[i].PartitionNumber = DriveLayout->PartitionEntry[i].PartitionNumber;
439 DriveLayoutEx->PartitionEntry[i].RewritePartition = DriveLayout->PartitionEntry[i].RewritePartition;
440 DriveLayoutEx->PartitionEntry[i].Mbr.PartitionType = DriveLayout->PartitionEntry[i].PartitionType;
441 DriveLayoutEx->PartitionEntry[i].Mbr.BootIndicator = DriveLayout->PartitionEntry[i].BootIndicator;
442 DriveLayoutEx->PartitionEntry[i].Mbr.RecognizedPartition = DriveLayout->PartitionEntry[i].RecognizedPartition;
443 DriveLayoutEx->PartitionEntry[i].Mbr.HiddenSectors = DriveLayout->PartitionEntry[i].HiddenSectors;
444 }
445
446 /* Finally, return data and free old structure */
447 *ReturnedDriveLayout = DriveLayoutEx;
448 ExFreePool(DriveLayout);
449
450 return STATUS_SUCCESS;
451 }
452
453 NTSTATUS
454 NTAPI
455 FstubReadSector(IN PDEVICE_OBJECT DeviceObject,
456 IN ULONG SectorSize,
457 IN ULONGLONG StartingSector OPTIONAL,
458 OUT PUSHORT Buffer)
459 {
460 PIRP Irp;
461 KEVENT Event;
462 NTSTATUS Status;
463 LARGE_INTEGER StartingOffset;
464 IO_STATUS_BLOCK IoStatusBlock;
465 PIO_STACK_LOCATION IoStackLocation;
466 PAGED_CODE();
467
468 ASSERT(DeviceObject);
469 ASSERT(Buffer);
470 ASSERT(SectorSize);
471
472 /* Compute starting offset */
473 StartingOffset.QuadPart = StartingSector * SectorSize;
474
475 /* Initialize waiting event */
476 KeInitializeEvent(&Event, NotificationEvent, FALSE);
477
478 /* Prepare IRP */
479 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
480 DeviceObject,
481 Buffer,
482 SectorSize,
483 &StartingOffset,
484 &Event,
485 &IoStatusBlock);
486 if (!Irp)
487 {
488 return STATUS_INSUFFICIENT_RESOURCES;
489 }
490
491 /* Override volume verify */
492 IoStackLocation = IoGetNextIrpStackLocation(Irp);
493 IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
494
495 /* Then call driver, and wait for completion if needed */
496 Status = IoCallDriver(DeviceObject, Irp);
497 if (Status == STATUS_PENDING)
498 {
499 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
500 Status = IoStatusBlock.Status;
501 }
502
503 return Status;
504 }
505
506 /* FUNCTIONS *****************************************************************/
507
508 /*
509 * @unimplemented
510 */
511 NTSTATUS
512 NTAPI
513 IoCreateDisk(IN PDEVICE_OBJECT DeviceObject,
514 IN struct _CREATE_DISK* Disk)
515 {
516 UNIMPLEMENTED;
517 return STATUS_NOT_IMPLEMENTED;
518 }
519
520 /*
521 * @unimplemented
522 */
523 NTSTATUS
524 NTAPI
525 IoGetBootDiskInformation(IN OUT PBOOTDISK_INFORMATION BootDiskInformation,
526 IN ULONG Size)
527 {
528 UNIMPLEMENTED;
529 return STATUS_NOT_IMPLEMENTED;
530 }
531
532 /*
533 * @unimplemented
534 */
535 NTSTATUS
536 NTAPI
537 IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject,
538 IN ULONG BytesPerSector,
539 OUT PDISK_SIGNATURE Signature)
540 {
541 UNIMPLEMENTED;
542 return STATUS_NOT_IMPLEMENTED;
543 }
544
545 /*
546 * @implemented
547 */
548 NTSTATUS
549 NTAPI
550 IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
551 IN struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
552 {
553 NTSTATUS Status;
554 PDISK_INFORMATION Disk;
555 PARTITION_STYLE PartitionStyle;
556 PAGED_CODE();
557
558 ASSERT(DeviceObject);
559 ASSERT(DriveLayout);
560
561 /* First of all, allocate internal structure */
562 Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0);
563 if (!NT_SUCCESS(Status))
564 {
565 return Status;
566 }
567 ASSERT(Disk);
568
569 /* Then, detect partition style (MBR? GTP/EFI? RAW?) */
570 Status = FstubDetectPartitionStyle(Disk, &PartitionStyle);
571 if (!NT_SUCCESS(Status))
572 {
573 FstubFreeDiskInformation(Disk);
574 return Status;
575 }
576
577 /* Here partition table is really read, depending on its style */
578 switch (PartitionStyle)
579 {
580 case PARTITION_STYLE_MBR:
581 case PARTITION_STYLE_RAW:
582 Status = FstubReadPartitionTableMBR(Disk, FALSE, DriveLayout);
583 break;
584
585 case PARTITION_STYLE_GPT:
586 /* Read primary table */
587 Status = FstubReadPartitionTableEFI(Disk, FALSE, DriveLayout);
588 /* If it failed, try reading backup table */
589 if (!NT_SUCCESS(Status))
590 {
591 Status = FstubReadPartitionTableEFI(Disk, TRUE, DriveLayout);
592 }
593 break;
594
595 default:
596 DPRINT("Unknown partition type\n");
597 Status = STATUS_UNSUCCESSFUL;
598 }
599
600 /* It's over, internal structure not needed anymore */
601 FstubFreeDiskInformation(Disk);
602
603 /* In case of success, print data */
604 if (NT_SUCCESS(Status))
605 {
606 FstubDbgPrintDriveLayoutEx(*DriveLayout);
607 }
608
609 return Status;
610 }
611
612 /*
613 * @unimplemented
614 */
615 NTSTATUS
616 NTAPI
617 IoSetPartitionInformationEx(IN PDEVICE_OBJECT DeviceObject,
618 IN ULONG PartitionNumber,
619 IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo)
620 {
621 UNIMPLEMENTED;
622 return STATUS_NOT_IMPLEMENTED;
623 }
624
625 /*
626 * @unimplemented
627 */
628 NTSTATUS
629 NTAPI
630 IoVerifyPartitionTable(IN PDEVICE_OBJECT DeviceObject,
631 IN BOOLEAN FixErrors)
632 {
633 UNIMPLEMENTED;
634 return STATUS_NOT_IMPLEMENTED;
635 }
636
637 /*
638 * @unimplemented
639 */
640 NTSTATUS
641 NTAPI
642 IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
643 IN struct _DRIVE_LAYOUT_INFORMATION_EX* DriveLayfout)
644 {
645 UNIMPLEMENTED;
646 return STATUS_NOT_IMPLEMENTED;
647 }
648
649 /* EOF */