#define EFI_HEADER_REVISION_1 0x00010000
/* Defines system type for MBR showing that a GPT is following */
#define EFI_PMBR_OSTYPE_EFI 0xEE
+/* Defines size to store a complete GUID + null char */
+#define EFI_GUID_STRING_SIZE 0x27
#define IS_VALID_DISK_INFO(Disk) \
(Disk) && \
FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk
);
+NTSTATUS
+NTAPI
+FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk,
+ IN ULONG PartitionsSizeSector,
+ IN GUID DiskGUID,
+ IN ULONG NumberOfEntries,
+ IN ULONGLONG FirstUsableLBA,
+ IN ULONGLONG LastUsableLBA,
+ IN ULONG PartitionEntryCRC32,
+ IN BOOLEAN WriteBackupTable);
+
NTSTATUS
NTAPI
FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk,
}
else
{
- DiskInformation->DiskGeometry = *DiskGeometry;
+ RtlCopyMemory(&DiskInformation->DiskGeometry, DiskGeometry, sizeof(DISK_GEOMETRY_EX));
}
/* Ensure read/received information are correct */
FstubDbgPrintDriveLayoutEx(IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout)
{
ULONG i;
- CHAR Guid[38];
+ CHAR Guid[EFI_GUID_STRING_SIZE];
PAGED_CODE();
DPRINT("FSTUB: DRIVE_LAYOUT_INFORMATION_EX: %p\n", DriveLayout);
case PARTITION_STYLE_MBR:
if (DriveLayout->PartitionCount % 4 != 0)
{
- DPRINT("Warning: Partition count isn't a 4-factor: %ld!\n", DriveLayout->PartitionCount);
+ DPRINT("Warning: Partition count isn't a 4-factor: %lu!\n", DriveLayout->PartitionCount);
}
DPRINT("Signature: %8.8x\n", DriveLayout->Mbr.Signature);
DPRINT("DiskId: %s\n", Guid);
DPRINT("StartingUsableOffset: %I64x\n", DriveLayout->Gpt.StartingUsableOffset.QuadPart);
DPRINT("UsableLength: %I64x\n", DriveLayout->Gpt.UsableLength.QuadPart);
- DPRINT("MaxPartitionCount: %ld\n", DriveLayout->Gpt.MaxPartitionCount);
+ DPRINT("MaxPartitionCount: %lu\n", DriveLayout->Gpt.MaxPartitionCount);
for (i = 0; i < DriveLayout->PartitionCount; i++)
{
FstubDbgPrintPartitionEx(DriveLayout->PartitionEntry, i);
break;
default:
- DPRINT("Unsupported partition style: %ld\n", DriveLayout->PartitionStyle);
+ DPRINT("Unsupported partition style: %lu\n", DriveLayout->PartitionStyle);
}
}
FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry,
IN ULONG PartitionNumber)
{
- CHAR Guid[38];
+ CHAR Guid[EFI_GUID_STRING_SIZE];
PAGED_CODE();
- DPRINT("Printing partition %ld\n", PartitionNumber);
+ DPRINT("Printing partition %lu\n", PartitionNumber);
switch (PartitionEntry[PartitionNumber].PartitionStyle)
{
case PARTITION_STYLE_MBR:
DPRINT(" StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
DPRINT(" PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
- DPRINT(" RewritePartition: %d\n", PartitionEntry[PartitionNumber].RewritePartition);
+ DPRINT(" RewritePartition: %u\n", PartitionEntry[PartitionNumber].RewritePartition);
DPRINT(" PartitionType: %02x\n", PartitionEntry[PartitionNumber].Mbr.PartitionType);
- DPRINT(" BootIndicator: %d\n", PartitionEntry[PartitionNumber].Mbr.BootIndicator);
- DPRINT(" RecognizedPartition: %d\n", PartitionEntry[PartitionNumber].Mbr.RecognizedPartition);
- DPRINT(" HiddenSectors: %ld\n", PartitionEntry[PartitionNumber].Mbr.HiddenSectors);
+ DPRINT(" BootIndicator: %u\n", PartitionEntry[PartitionNumber].Mbr.BootIndicator);
+ DPRINT(" RecognizedPartition: %u\n", PartitionEntry[PartitionNumber].Mbr.RecognizedPartition);
+ DPRINT(" HiddenSectors: %lu\n", PartitionEntry[PartitionNumber].Mbr.HiddenSectors);
break;
case PARTITION_STYLE_GPT:
DPRINT(" StartingOffset: %I64x\n", PartitionEntry[PartitionNumber].StartingOffset.QuadPart);
DPRINT(" PartitionLength: %I64x\n", PartitionEntry[PartitionNumber].PartitionLength.QuadPart);
- DPRINT(" RewritePartition: %d\n", PartitionEntry[PartitionNumber].RewritePartition);
+ DPRINT(" RewritePartition: %u\n", PartitionEntry[PartitionNumber].RewritePartition);
FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionType), Guid);
DPRINT(" PartitionType: %s\n", Guid);
FstubDbgGuidToString(&(PartitionEntry[PartitionNumber].Gpt.PartitionId), Guid);
DPRINT(" PartitionId: %s\n", Guid);
- DPRINT(" Attributes: %16x\n", PartitionEntry[PartitionNumber].Gpt.Attributes);
+ DPRINT(" Attributes: %I64x\n", PartitionEntry[PartitionNumber].Gpt.Attributes);
DPRINT(" Name: %ws\n", PartitionEntry[PartitionNumber].Gpt.Name);
break;
FstubDbgPrintSetPartitionEx(IN PSET_PARTITION_INFORMATION_EX PartitionEntry,
IN ULONG PartitionNumber)
{
- CHAR Guid[38];
+ CHAR Guid[EFI_GUID_STRING_SIZE];
PAGED_CODE();
DPRINT("FSTUB: SET_PARTITION_INFORMATION_EX: %p\n", PartitionEntry);
- DPRINT("Modifying partition %ld\n", PartitionNumber);
+ DPRINT("Modifying partition %lu\n", PartitionNumber);
switch (PartitionEntry->PartitionStyle)
{
case PARTITION_STYLE_MBR:
DPRINT(" PartitionType: %s\n", Guid);
FstubDbgGuidToString(&(PartitionEntry->Gpt.PartitionId), Guid);
DPRINT(" PartitionId: %s\n", Guid);
- DPRINT(" Attributes: %16x\n", PartitionEntry->Gpt.Attributes);
+ DPRINT(" Attributes: %I64x\n", PartitionEntry->Gpt.Attributes);
DPRINT(" Name: %ws\n", PartitionEntry->Gpt.Name);
break;
NTAPI
FstubReadHeaderEFI(IN PDISK_INFORMATION Disk,
IN BOOLEAN ReadBackupTable,
- PEFI_PARTITION_HEADER HeaderBuffer)
+ PEFI_PARTITION_HEADER * HeaderBuffer)
{
NTSTATUS Status;
PUCHAR Sector = NULL;
if (PreviousCRC32 == EFIHeader->PartitionEntryCRC32)
{
/* In case of a success, return read header */
- *HeaderBuffer = *EFIHeader;
+ *HeaderBuffer = EFIHeader;
return STATUS_SUCCESS;
}
else
OUT struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout)
{
NTSTATUS Status;
- EFI_PARTITION_HEADER EfiHeader;
- ULONGLONG SectorsForPartitions;
+ ULONG NumberOfEntries;
+ PEFI_PARTITION_HEADER EfiHeader;
EFI_PARTITION_ENTRY PartitionEntry;
+#if 0
BOOLEAN UpdatedPartitionTable = FALSE;
+ ULONGLONG SectorsForPartitions, PartitionEntryLBA;
+#else
+ ULONGLONG PartitionEntryLBA;
+#endif
PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL;
ULONG i, PartitionCount, PartitionIndex, PartitionsPerSector;
PAGED_CODE();
return Status;
}
+ /* Backup the number of entries, will be used later on */
+ NumberOfEntries = EfiHeader->NumberOfEntries;
+
/* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */
DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool,
FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
- EfiHeader.NumberOfEntries * sizeof(PARTITION_INFORMATION_EX),
+ EfiHeader->NumberOfEntries * sizeof(PARTITION_INFORMATION_EX),
TAG_FSTUB);
if (!DriveLayoutEx)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
- if (ReadBackupTable)
+#if 0
+ if (!ReadBackupTable)
{
- /* If we read backup but if it doesn't match with current geometry */
- if ((Disk->SectorCount - 1ULL) != EfiHeader.AlternateLBA)
+ /* If we weren't ask to read backup table,
+ * check the status of the backup table.
+ * In case it's not where we're expecting it, move it and ask
+ * for a partition table rewrite.
+ */
+ if ((Disk->SectorCount - 1ULL) != EfiHeader->AlternateLBA)
{
/* We'll update it. First, count number of sectors needed to store partitions */
- SectorsForPartitions = ((ULONGLONG)EfiHeader.NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
+ SectorsForPartitions = ((ULONGLONG)EfiHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
/* Then set first usable LBA: Legacy MBR + GPT header + Partitions entries */
- EfiHeader.FirstUsableLBA = SectorsForPartitions + 2;
+ EfiHeader->FirstUsableLBA = SectorsForPartitions + 2;
/* Then set last usable LBA: Last sector - GPT header - Partitions entries */
- EfiHeader.LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
+ EfiHeader->LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
/* Inform that we'll rewrite partition table */
UpdatedPartitionTable = TRUE;
}
}
+#endif
DriveLayoutEx->PartitionStyle = PARTITION_STYLE_GPT;
/* Translate LBA -> Offset */
- DriveLayoutEx->Gpt.StartingUsableOffset.QuadPart = EfiHeader.FirstUsableLBA * Disk->SectorSize;
- DriveLayoutEx->Gpt.UsableLength.QuadPart = EfiHeader.LastUsableLBA - EfiHeader.FirstUsableLBA * Disk->SectorSize;
- DriveLayoutEx->Gpt.MaxPartitionCount = EfiHeader.NumberOfEntries;
- DriveLayoutEx->Gpt.DiskId = EfiHeader.DiskGUID;
+ DriveLayoutEx->Gpt.StartingUsableOffset.QuadPart = EfiHeader->FirstUsableLBA * Disk->SectorSize;
+ DriveLayoutEx->Gpt.UsableLength.QuadPart = EfiHeader->LastUsableLBA - EfiHeader->FirstUsableLBA * Disk->SectorSize;
+ DriveLayoutEx->Gpt.MaxPartitionCount = EfiHeader->NumberOfEntries;
+ DriveLayoutEx->Gpt.DiskId = EfiHeader->DiskGUID;
+ /* Backup partition entry position */
+ PartitionEntryLBA = EfiHeader->PartitionEntryLBA;
/* Count number of partitions per sector */
PartitionsPerSector = (Disk->SectorSize / PARTITION_ENTRY_SIZE);
- /* Read all partitions and fill in structure */
+ /* Read all partitions and fill in structure
+ * BEWARE! Past that point EfiHeader IS NOT VALID ANYMORE
+ * It will be erased by the reading of the partition entry
+ */
for (i = 0, PartitionCount = 0, PartitionIndex = PartitionsPerSector;
- i < EfiHeader.NumberOfEntries;
+ i < NumberOfEntries;
i++)
{
/* Only read following sector if we finished with previous sector */
{
Status = FstubReadSector(Disk->DeviceObject,
Disk->SectorSize,
- EfiHeader.PartitionEntryLBA + (i / PartitionsPerSector),
+ PartitionEntryLBA + (i / PartitionsPerSector),
Disk->Buffer);
if (!NT_SUCCESS(Status))
{
}
DriveLayoutEx->PartitionCount = PartitionCount;
+#if 0
/* If we updated partition table using backup table, rewrite partition table */
if (UpdatedPartitionTable)
{
IoWritePartitionTableEx(Disk->DeviceObject,
DriveLayoutEx);
}
+#endif
/* Finally, return read data */
*DriveLayout = DriveLayoutEx;
IN BOOLEAN FixErrors)
{
NTSTATUS Status;
- PEFI_PARTITION_HEADER EFIHeader;
- EFI_PARTITION_HEADER ReadEFIHeader;
- BOOLEAN PrimaryValid = FALSE, BackupValid = FALSE;
+ PEFI_PARTITION_HEADER EFIHeader, ReadEFIHeader;
+ BOOLEAN PrimaryValid = FALSE, BackupValid = FALSE, WriteBackup;
+ ULONGLONG ReadPosition, WritePosition, SectorsForPartitions, PartitionIndex;
PAGED_CODE();
EFIHeader = ExAllocatePoolWithTag(NonPagedPool, sizeof(EFI_PARTITION_HEADER), TAG_FSTUB);
if (NT_SUCCESS(Status))
{
PrimaryValid = TRUE;
+ ASSERT(ReadEFIHeader);
+ RtlCopyMemory(EFIHeader, ReadEFIHeader, sizeof(EFI_PARTITION_HEADER));
}
Status = FstubReadHeaderEFI(Disk, TRUE, &ReadEFIHeader);
if (NT_SUCCESS(Status))
{
BackupValid = TRUE;
+ ASSERT(ReadEFIHeader);
+ RtlCopyMemory(EFIHeader, ReadEFIHeader, sizeof(EFI_PARTITION_HEADER));
}
- if (!PrimaryValid)
+ /* If both are sane, just return */
+ if (PrimaryValid && BackupValid)
{
- if (!BackupValid || !FixErrors)
- {
- ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
- return STATUS_DISK_CORRUPT_ERROR;
- }
+ ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
+ return STATUS_SUCCESS;
+ }
- DPRINT1("EFI::Partition table fixing not yet supported!\n");
+ /* If both are damaged OR if we have not been ordered to fix
+ * Then, quit and warn about disk corruption
+ */
+ if ((!PrimaryValid && !BackupValid) || !FixErrors)
+ {
ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
- return STATUS_NOT_IMPLEMENTED;
+ return STATUS_DISK_CORRUPT_ERROR;
}
- else if (!BackupValid)
+
+ /* Compute sectors taken by partitions */
+ SectorsForPartitions = (((ULONGLONG)EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) + Disk->SectorSize - 1) / Disk->SectorSize;
+ if (PrimaryValid)
{
- if (!PrimaryValid || !FixErrors)
- {
- ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
- return STATUS_DISK_CORRUPT_ERROR;
- }
+ WriteBackup = TRUE;
+ /* Take position at backup table for writing */
+ WritePosition = Disk->SectorCount - SectorsForPartitions;
+ /* And read from primary table */
+ ReadPosition = 2ULL;
- DPRINT1("EFI::Partition table fixing not yet supported!\n");
- ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
- return STATUS_NOT_IMPLEMENTED;
+ DPRINT("EFI::Will repair backup table from primary\n");
}
else
{
- ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
- return STATUS_SUCCESS;
+ ASSERT(BackupValid);
+ WriteBackup = FALSE;
+ /* Take position at primary table for writing */
+ WritePosition = 2ULL;
+ /* And read from backup table */
+ ReadPosition = Disk->SectorCount - SectorsForPartitions;
+
+ DPRINT("EFI::Will repair primary table from backup\n");
}
+
+ PartitionIndex = 0ULL;
+
+ /* If no partitions are to be copied, just restore header */
+ if (SectorsForPartitions <= 0)
+ {
+ Status = FstubWriteHeaderEFI(Disk,
+ SectorsForPartitions,
+ EFIHeader->DiskGUID,
+ EFIHeader->NumberOfEntries,
+ EFIHeader->FirstUsableLBA,
+ EFIHeader->LastUsableLBA,
+ EFIHeader->PartitionEntryCRC32,
+ WriteBackup);
+
+ goto Cleanup;
+ }
+
+ /* Copy all the partitions */
+ for (; PartitionIndex < SectorsForPartitions; ++PartitionIndex)
+ {
+ /* First, read the partition from the first table */
+ Status = FstubReadSector(Disk->DeviceObject,
+ Disk->SectorSize,
+ ReadPosition + PartitionIndex,
+ Disk->Buffer);
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+
+ /* Then, write it in the other table */
+ Status = FstubWriteSector(Disk->DeviceObject,
+ Disk->SectorSize,
+ WritePosition + PartitionIndex,
+ Disk->Buffer);
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+ }
+
+ /* Now we're done, write the header */
+ Status = FstubWriteHeaderEFI(Disk,
+ SectorsForPartitions,
+ EFIHeader->DiskGUID,
+ EFIHeader->NumberOfEntries,
+ EFIHeader->FirstUsableLBA,
+ EFIHeader->LastUsableLBA,
+ EFIHeader->PartitionEntryCRC32,
+ WriteBackup);
+
+Cleanup:
+ ExFreePoolWithTag(EFIHeader, TAG_FSTUB);
+ return Status;
}
NTSTATUS
/* Copy the entry at the proper place into the buffer
* That way, we don't erase previous entries
*/
- RtlCopyMemory(Disk->Buffer + (((PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize) / sizeof(PUSHORT)),
+ RtlCopyMemory((PVOID)((ULONG_PTR)Disk->Buffer + ((PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize)),
PartitionEntry,
sizeof(EFI_PARTITION_ENTRY));
/* Compute size of buffer */
{
return Status;
}
+
/* We clean buffer */
RtlZeroMemory(Disk->Buffer, Disk->SectorSize);
}
/* Debug the way we'll break disk, to let user pray */
DPRINT("FSTUB: About to write the following header for %s table\n", (WriteBackupTable ? "backup" : "primary"));
- DPRINT(" Signature: %I64x\n Revision: %x\n HeaderSize: %x\n HeaderCRC32: %x\n",
- EFIHeader->Signature, EFIHeader->Revision, EFIHeader->HeaderSize, EFIHeader->HeaderCRC32);
- DPRINT(" MyLBA: %I64x\n AlternateLBA: %I64x\n FirstUsableLBA: %I64x\n LastUsableLBA: %I64x\n",
- EFIHeader->MyLBA, EFIHeader->AlternateLBA, EFIHeader->FirstUsableLBA, EFIHeader->LastUsableLBA);
- DPRINT(" PartitionEntryLBA: %I64x\n NumberOfEntries: %x\n SizeOfPartitionEntry: %x\n PartitionEntryCRC32: %x\n",
- EFIHeader->PartitionEntryLBA, EFIHeader->NumberOfEntries,
- EFIHeader->SizeOfPartitionEntry, EFIHeader->PartitionEntryCRC32);
+ DPRINT(" Signature: %I64x\n", EFIHeader->Signature);
+ DPRINT(" Revision: %x\n", EFIHeader->Revision);
+ DPRINT(" HeaderSize: %x\n", EFIHeader->HeaderSize);
+ DPRINT(" HeaderCRC32: %x\n", EFIHeader->HeaderCRC32);
+ DPRINT(" MyLBA: %I64x\n", EFIHeader->MyLBA);
+ DPRINT(" AlternateLBA: %I64x\n", EFIHeader->AlternateLBA);
+ DPRINT(" FirstUsableLBA: %I64x\n", EFIHeader->FirstUsableLBA);
+ DPRINT(" LastUsableLBA: %I64x\n", EFIHeader->LastUsableLBA);
+ DPRINT(" PartitionEntryLBA: %I64x\n", EFIHeader->PartitionEntryLBA);
+ DPRINT(" NumberOfEntries: %x\n", EFIHeader->NumberOfEntries);
+ DPRINT(" SizeOfPartitionEntry: %x\n", EFIHeader->SizeOfPartitionEntry);
+ DPRINT(" PartitionEntryCRC32: %x\n", EFIHeader->PartitionEntryCRC32);
/* Write header to disk */
return FstubWriteSector(Disk->DeviceObject,
IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject,
IN struct _DRIVE_LAYOUT_INFORMATION_EX* DriveLayout)
{
+ GUID DiskGuid;
NTSTATUS Status;
+ ULONG NumberOfEntries;
PDISK_INFORMATION Disk;
- ULONGLONG SectorsForPartitions;
- EFI_PARTITION_HEADER EfiHeader;
+ PEFI_PARTITION_HEADER EfiHeader;
+ ULONGLONG SectorsForPartitions, FirstUsableLBA, LastUsableLBA;
PAGED_CODE();
ASSERT(DeviceObject);
if (NT_SUCCESS(Status))
{
/* Check if there are enough places for the partitions to be written */
- if (DriveLayout->PartitionCount <= EfiHeader.NumberOfEntries)
+ if (DriveLayout->PartitionCount <= EfiHeader->NumberOfEntries)
{
+ /* Backup data */
+ NumberOfEntries = EfiHeader->NumberOfEntries;
+ RtlCopyMemory(&DiskGuid, &EfiHeader->DiskGUID, sizeof(GUID));
/* Count number of sectors needed to store partitions */
- SectorsForPartitions = (EfiHeader.NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
+ SectorsForPartitions = ((ULONGLONG)NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize;
/* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */
- EfiHeader.FirstUsableLBA = SectorsForPartitions + 2;
+ FirstUsableLBA = SectorsForPartitions + 2;
/* Set last usable LBA: Last sector - GPT header - Partitions entries */
- EfiHeader.LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
+ LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1;
/* Write primary table */
Status = FstubWritePartitionTableEFI(Disk,
- EfiHeader.DiskGUID,
- EfiHeader.NumberOfEntries,
- EfiHeader.FirstUsableLBA,
- EfiHeader.LastUsableLBA,
+ DiskGuid,
+ NumberOfEntries,
+ FirstUsableLBA,
+ LastUsableLBA,
FALSE,
DriveLayout->PartitionCount,
DriveLayout->PartitionEntry);
if (NT_SUCCESS(Status))
{
Status = FstubWritePartitionTableEFI(Disk,
- EfiHeader.DiskGUID,
- EfiHeader.NumberOfEntries,
- EfiHeader.FirstUsableLBA,
- EfiHeader.LastUsableLBA,
+ DiskGuid,
+ NumberOfEntries,
+ FirstUsableLBA,
+ LastUsableLBA,
TRUE,
DriveLayout->PartitionCount,
DriveLayout->PartitionEntry);
}
}
+ else
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
}
break;
default:
- DPRINT("Unsupported partition style: %ld\n", DriveLayout->PartitionStyle);
+ DPRINT("Unsupported partition style: %lu\n", DriveLayout->PartitionStyle);
Status = STATUS_NOT_SUPPORTED;
}