mirror of
https://github.com/LongSoft/UEFITool.git
synced 2024-11-24 17:08:23 +08:00
Fix obvious mistakes and avoid the use of unimplemented parser data for compression algo
This commit is contained in:
parent
0a1987fcde
commit
717821de2b
@ -159,8 +159,6 @@ void UEFITool::init()
|
|||||||
ui->actionToggleBootGuardMarking->setChecked(markingEnabled);
|
ui->actionToggleBootGuardMarking->setChecked(markingEnabled);
|
||||||
|
|
||||||
// Connect
|
// Connect
|
||||||
connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const UModelIndex &, const UModelIndex &)),
|
|
||||||
this, SLOT(populateUi(const UModelIndex &)));
|
|
||||||
connect(ui->structureTreeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
connect(ui->structureTreeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
||||||
this, SLOT(populateUi(const QItemSelection &)));
|
this, SLOT(populateUi(const QItemSelection &)));
|
||||||
connect(ui->parserMessagesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*)));
|
connect(ui->parserMessagesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*)));
|
||||||
|
@ -69,6 +69,7 @@ typedef size_t USTATUS;
|
|||||||
#define U_ELEMENTS_NOT_FOUND 47
|
#define U_ELEMENTS_NOT_FOUND 47
|
||||||
#define U_PEI_CORE_ENTRY_POINT_NOT_FOUND 48
|
#define U_PEI_CORE_ENTRY_POINT_NOT_FOUND 48
|
||||||
#define U_INVALID_STORE_SIZE 49
|
#define U_INVALID_STORE_SIZE 49
|
||||||
|
#define U_UNKNOWN_COMPRESSION_ALGORITHM 50
|
||||||
#define U_NOT_IMPLEMENTED 0xFF
|
#define U_NOT_IMPLEMENTED 0xFF
|
||||||
|
|
||||||
// UDK porting definitions
|
// UDK porting definitions
|
||||||
|
@ -1287,7 +1287,6 @@ USTATUS FfsBuilder::buildSection(const UModelIndex & index, const UINT32 base, U
|
|||||||
EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)header.data();
|
EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)header.data();
|
||||||
bool extended = false;
|
bool extended = false;
|
||||||
UByteArray data = model->parsingData(index);
|
UByteArray data = model->parsingData(index);
|
||||||
const COMPRESSED_SECTION_PARSING_DATA* compress_data = (const COMPRESSED_SECTION_PARSING_DATA*)data.constData();
|
|
||||||
|
|
||||||
if (uint24ToUint32(commonHeader->Size) == 0xFFFFFF) {
|
if (uint24ToUint32(commonHeader->Size) == 0xFFFFFF) {
|
||||||
extended = true;
|
extended = true;
|
||||||
@ -1327,17 +1326,26 @@ USTATUS FfsBuilder::buildSection(const UModelIndex & index, const UINT32 base, U
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only this 2 sections can have compressed body
|
// Only this 2 sections can have compressed body
|
||||||
|
UINT8 compression = model->compressed(index) ? model->compression(index) : COMPRESSION_ALGORITHM_NONE;
|
||||||
if (model->subtype(index) == EFI_SECTION_COMPRESSION) {
|
if (model->subtype(index) == EFI_SECTION_COMPRESSION) {
|
||||||
EFI_COMPRESSION_SECTION* compessionHeader = (EFI_COMPRESSION_SECTION*)header.data();
|
EFI_COMPRESSION_SECTION* compessionHeader = (EFI_COMPRESSION_SECTION*)header.data();
|
||||||
// Set new uncompressed size
|
// Set new uncompressed size
|
||||||
compessionHeader->UncompressedLength = reconstructed.size();
|
compessionHeader->UncompressedLength = reconstructed.size();
|
||||||
// Compress new section body
|
// Compress new section body
|
||||||
UByteArray compressed;
|
UByteArray compressed;
|
||||||
result = compress(reconstructed, compress_data->algorithm, compressed);
|
result = compress(reconstructed, compression, compressed);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
// Correct compression type
|
// Correct compression type
|
||||||
compessionHeader->CompressionType = compress_data->compressionType;
|
if (compression == COMPRESSION_ALGORITHM_NONE)
|
||||||
|
compessionHeader->CompressionType = EFI_NOT_COMPRESSED;
|
||||||
|
else if (compression == COMPRESSION_ALGORITHM_LZMA || compression == COMPRESSION_ALGORITHM_IMLZMA)
|
||||||
|
compessionHeader->CompressionType = EFI_CUSTOMIZED_COMPRESSION;
|
||||||
|
else if (compression == COMPRESSION_ALGORITHM_EFI11 || compression == COMPRESSION_ALGORITHM_TIANO)
|
||||||
|
compessionHeader->CompressionType = EFI_STANDARD_COMPRESSION;
|
||||||
|
else
|
||||||
|
return U_UNKNOWN_COMPRESSION_ALGORITHM;
|
||||||
|
|
||||||
// Replace new section body
|
// Replace new section body
|
||||||
reconstructed = compressed;
|
reconstructed = compressed;
|
||||||
@ -1346,7 +1354,7 @@ USTATUS FfsBuilder::buildSection(const UModelIndex & index, const UINT32 base, U
|
|||||||
EFI_GUID_DEFINED_SECTION* guidDefinedHeader = (EFI_GUID_DEFINED_SECTION*)header.data();
|
EFI_GUID_DEFINED_SECTION* guidDefinedHeader = (EFI_GUID_DEFINED_SECTION*)header.data();
|
||||||
// Compress new section body
|
// Compress new section body
|
||||||
UByteArray compressed;
|
UByteArray compressed;
|
||||||
result = compress(reconstructed, compress_data->algorithm, compressed);
|
result = compress(reconstructed, compression, compressed);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
// Check for authentication status valid attribute
|
// Check for authentication status valid attribute
|
||||||
@ -1377,7 +1385,7 @@ USTATUS FfsBuilder::buildSection(const UModelIndex & index, const UINT32 base, U
|
|||||||
// Replace new section body
|
// Replace new section body
|
||||||
reconstructed = compressed;
|
reconstructed = compressed;
|
||||||
}
|
}
|
||||||
else if (compress_data->algorithm != COMPRESSION_ALGORITHM_NONE) {
|
else if (compression != COMPRESSION_ALGORITHM_NONE) {
|
||||||
msg(usprintf("buildSection: incorrectly required compression for section of type %1")
|
msg(usprintf("buildSection: incorrectly required compression for section of type %1")
|
||||||
.arg(model->subtype(index)), index);
|
.arg(model->subtype(index)), index);
|
||||||
return U_INVALID_SECTION;
|
return U_INVALID_SECTION;
|
||||||
|
@ -2493,8 +2493,10 @@ USTATUS FfsParser::parseCompressedSectionBody(const UModelIndex & index)
|
|||||||
pdata.uncompressedSize = uncompressedSize;
|
pdata.uncompressedSize = uncompressedSize;
|
||||||
model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
|
model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
|
||||||
|
|
||||||
if (algorithm != COMPRESSION_ALGORITHM_NONE)
|
if (algorithm != COMPRESSION_ALGORITHM_NONE) {
|
||||||
model->setCompressed(index, true);
|
model->setCompressed(index, true);
|
||||||
|
model->setCompression(index, algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
// Parse decompressed data
|
// Parse decompressed data
|
||||||
return parseSections(decompressed, index, true);
|
return parseSections(decompressed, index, true);
|
||||||
@ -2571,8 +2573,10 @@ USTATUS FfsParser::parseGuidedSectionBody(const UModelIndex & index)
|
|||||||
model->addInfo(index, info);
|
model->addInfo(index, info);
|
||||||
|
|
||||||
// Update data
|
// Update data
|
||||||
if (algorithm != COMPRESSION_ALGORITHM_NONE)
|
if (algorithm != COMPRESSION_ALGORITHM_NONE) {
|
||||||
model->setCompressed(index, true);
|
model->setCompressed(index, true);
|
||||||
|
model->setCompression(index, algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
if (!parseCurrentSection) {
|
if (!parseCurrentSection) {
|
||||||
msg(usprintf("%s: GUID defined section can not be processed", __FUNCTION__), index);
|
msg(usprintf("%s: GUID defined section can not be processed", __FUNCTION__), index);
|
||||||
@ -2614,32 +2618,38 @@ USTATUS FfsParser::parseDepexSectionBody(const UModelIndex & index)
|
|||||||
|
|
||||||
// Special cases of first opcode
|
// Special cases of first opcode
|
||||||
switch (*current) {
|
switch (*current) {
|
||||||
case EFI_DEP_BEFORE:
|
case EFI_DEP_BEFORE: {
|
||||||
if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) {
|
if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) {
|
||||||
msg(usprintf("%s: DEPEX section too long for a section starting with BEFORE opcode", __FUNCTION__), index);
|
msg(usprintf("%s: DEPEX section too long for a section starting with BEFORE opcode", __FUNCTION__), index);
|
||||||
return U_SUCCESS;
|
return U_SUCCESS;
|
||||||
}
|
}
|
||||||
guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE);
|
guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE);
|
||||||
parsed += UString("\nBEFORE ") + guidToUString(*guid);
|
EFI_GUID tmpGuid;
|
||||||
|
memcpy(&tmpGuid, guid, sizeof(EFI_GUID));
|
||||||
|
parsed += UString("\nBEFORE ") + guidToUString(tmpGuid);
|
||||||
current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID);
|
current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID);
|
||||||
if (*current != EFI_DEP_END){
|
if (*current != EFI_DEP_END){
|
||||||
msg(usprintf("%s: DEPEX section ends with non-END opcode", __FUNCTION__), index);
|
msg(usprintf("%s: DEPEX section ends with non-END opcode", __FUNCTION__), index);
|
||||||
return U_SUCCESS;
|
return U_SUCCESS;
|
||||||
}
|
}
|
||||||
return U_SUCCESS;
|
return U_SUCCESS;
|
||||||
case EFI_DEP_AFTER:
|
}
|
||||||
|
case EFI_DEP_AFTER: {
|
||||||
if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)){
|
if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)){
|
||||||
msg(usprintf("%s: DEPEX section too long for a section starting with AFTER opcode", __FUNCTION__), index);
|
msg(usprintf("%s: DEPEX section too long for a section starting with AFTER opcode", __FUNCTION__), index);
|
||||||
return U_SUCCESS;
|
return U_SUCCESS;
|
||||||
}
|
}
|
||||||
guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE);
|
guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE);
|
||||||
parsed += UString("\nAFTER ") + guidToUString(*guid);
|
EFI_GUID tmpGuid;
|
||||||
|
memcpy(&tmpGuid, guid, sizeof(EFI_GUID));
|
||||||
|
parsed += UString("\nAFTER ") + guidToUString(tmpGuid);
|
||||||
current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID);
|
current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID);
|
||||||
if (*current != EFI_DEP_END) {
|
if (*current != EFI_DEP_END) {
|
||||||
msg(usprintf("%s: DEPEX section ends with non-END opcode", __FUNCTION__), index);
|
msg(usprintf("%s: DEPEX section ends with non-END opcode", __FUNCTION__), index);
|
||||||
return U_SUCCESS;
|
return U_SUCCESS;
|
||||||
}
|
}
|
||||||
return U_SUCCESS;
|
return U_SUCCESS;
|
||||||
|
}
|
||||||
case EFI_DEP_SOR:
|
case EFI_DEP_SOR:
|
||||||
if (body.size() <= 2 * EFI_DEP_OPCODE_SIZE) {
|
if (body.size() <= 2 * EFI_DEP_OPCODE_SIZE) {
|
||||||
msg(usprintf("%s: DEPEX section too short for a section starting with SOR opcode", __FUNCTION__), index);
|
msg(usprintf("%s: DEPEX section too short for a section starting with SOR opcode", __FUNCTION__), index);
|
||||||
@ -2665,7 +2675,7 @@ USTATUS FfsParser::parseDepexSectionBody(const UModelIndex & index)
|
|||||||
msg(usprintf("%s: misplaced SOR opcode", __FUNCTION__), index);
|
msg(usprintf("%s: misplaced SOR opcode", __FUNCTION__), index);
|
||||||
return U_SUCCESS;
|
return U_SUCCESS;
|
||||||
}
|
}
|
||||||
case EFI_DEP_PUSH:
|
case EFI_DEP_PUSH: {
|
||||||
// Check that the rest of depex has correct size
|
// Check that the rest of depex has correct size
|
||||||
if ((UINT32)body.size() - (UINT32)(current - (const UINT8*)body.constData()) <= EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) {
|
if ((UINT32)body.size() - (UINT32)(current - (const UINT8*)body.constData()) <= EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) {
|
||||||
parsed.clear();
|
parsed.clear();
|
||||||
@ -2673,9 +2683,12 @@ USTATUS FfsParser::parseDepexSectionBody(const UModelIndex & index)
|
|||||||
return U_SUCCESS;
|
return U_SUCCESS;
|
||||||
}
|
}
|
||||||
guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE);
|
guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE);
|
||||||
parsed += UString("\nPUSH ") + guidToUString(*guid);
|
EFI_GUID tmpGuid;
|
||||||
|
memcpy(&tmpGuid, guid, sizeof(EFI_GUID));
|
||||||
|
parsed += UString("\nPUSH ") + guidToUString(tmpGuid);
|
||||||
current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID);
|
current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case EFI_DEP_AND:
|
case EFI_DEP_AND:
|
||||||
parsed += UString("\nAND");
|
parsed += UString("\nAND");
|
||||||
current += EFI_DEP_OPCODE_SIZE;
|
current += EFI_DEP_OPCODE_SIZE;
|
||||||
|
@ -283,8 +283,10 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index)
|
|||||||
|
|
||||||
// The list begins at the end of the store and goes backwards
|
// The list begins at the end of the store and goes backwards
|
||||||
const EFI_GUID* guidPtr = (const EFI_GUID*)(data.constData() + data.size()) - 1 - guidIndex;
|
const EFI_GUID* guidPtr = (const EFI_GUID*)(data.constData() + data.size()) - 1 - guidIndex;
|
||||||
name = guidToUString(*guidPtr);
|
EFI_GUID tmpGuid;
|
||||||
guid = guidToUString(*guidPtr, false);
|
memcpy(&tmpGuid, guidPtr, sizeof(EFI_GUID));
|
||||||
|
name = guidToUString(tmpGuid);
|
||||||
|
guid = guidToUString(tmpGuid, false);
|
||||||
hasGuidIndex = true;
|
hasGuidIndex = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +367,7 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index)
|
|||||||
|
|
||||||
// Try parsing the entry data as NVAR storage if it begins with NVAR signature
|
// Try parsing the entry data as NVAR storage if it begins with NVAR signature
|
||||||
if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry)
|
if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry)
|
||||||
&& *(const UINT32*)body.constData() == NVRAM_NVAR_ENTRY_SIGNATURE)
|
&& body.size() >= 4 && *(const UINT32*)body.constData() == NVRAM_NVAR_ENTRY_SIGNATURE)
|
||||||
parseNvarStore(varIndex);
|
parseNvarStore(varIndex);
|
||||||
|
|
||||||
// Move to next exntry
|
// Move to next exntry
|
||||||
|
@ -32,6 +32,7 @@ TreeItem::TreeItem(const UINT32 offset, const UINT8 type, const UINT8 subtype,
|
|||||||
itemTail(tail),
|
itemTail(tail),
|
||||||
itemFixed(fixed),
|
itemFixed(fixed),
|
||||||
itemCompressed(compressed),
|
itemCompressed(compressed),
|
||||||
|
itemCompression(COMPRESSION_ALGORITHM_NONE),
|
||||||
parentItem(parent)
|
parentItem(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -98,4 +99,4 @@ TreeItem* TreeItem::child(int row)
|
|||||||
std::list<TreeItem*>::iterator child = childItems.begin();
|
std::list<TreeItem*>::iterator child = childItems.begin();
|
||||||
std::advance(child, row);
|
std::advance(child, row);
|
||||||
return *child;
|
return *child;
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,9 @@ public:
|
|||||||
bool compressed() const { return itemCompressed; }
|
bool compressed() const { return itemCompressed; }
|
||||||
void setCompressed(const bool compressed) { itemCompressed = compressed; }
|
void setCompressed(const bool compressed) { itemCompressed = compressed; }
|
||||||
|
|
||||||
|
bool compression() const { return itemCompression; }
|
||||||
|
void setCompression(const UINT8 compression) { itemCompression = compression; }
|
||||||
|
|
||||||
UByteArray parsingData() const { return itemParsingData; };
|
UByteArray parsingData() const { return itemParsingData; };
|
||||||
bool hasEmptyParsingData() const { return itemParsingData.isEmpty(); }
|
bool hasEmptyParsingData() const { return itemParsingData.isEmpty(); }
|
||||||
void setParsingData(const UByteArray & pdata) { itemParsingData = pdata; }
|
void setParsingData(const UByteArray & pdata) { itemParsingData = pdata; }
|
||||||
@ -104,6 +107,7 @@ private:
|
|||||||
UByteArray itemTail;
|
UByteArray itemTail;
|
||||||
bool itemFixed;
|
bool itemFixed;
|
||||||
bool itemCompressed;
|
bool itemCompressed;
|
||||||
|
UINT8 itemCompression;
|
||||||
UByteArray itemParsingData;
|
UByteArray itemParsingData;
|
||||||
TreeItem* parentItem;
|
TreeItem* parentItem;
|
||||||
};
|
};
|
||||||
|
@ -284,6 +284,14 @@ bool TreeModel::compressed(const UModelIndex &index) const
|
|||||||
return item->compressed();
|
return item->compressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UINT8 TreeModel::compression(const UModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return false;
|
||||||
|
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
|
||||||
|
return item->compression();
|
||||||
|
}
|
||||||
|
|
||||||
void TreeModel::setFixed(const UModelIndex &index, const bool fixed)
|
void TreeModel::setFixed(const UModelIndex &index, const bool fixed)
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
@ -319,6 +327,17 @@ void TreeModel::setCompressed(const UModelIndex &index, const bool compressed)
|
|||||||
emit dataChanged(index, index);
|
emit dataChanged(index, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TreeModel::setCompression(const UModelIndex &index, const UINT8 compression)
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
|
||||||
|
item->setCompression(compression);
|
||||||
|
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
}
|
||||||
|
|
||||||
void TreeModel::TreeModel::setMarkingEnabled(const bool enabled)
|
void TreeModel::TreeModel::setMarkingEnabled(const bool enabled)
|
||||||
{
|
{
|
||||||
markingEnabledFlag = enabled;
|
markingEnabledFlag = enabled;
|
||||||
|
@ -152,6 +152,7 @@ public:
|
|||||||
void addInfo(const UModelIndex &index, const UString &info, const bool append = TRUE);
|
void addInfo(const UModelIndex &index, const UString &info, const bool append = TRUE);
|
||||||
void setFixed(const UModelIndex &index, const bool fixed);
|
void setFixed(const UModelIndex &index, const bool fixed);
|
||||||
void setCompressed(const UModelIndex &index, const bool compressed);
|
void setCompressed(const UModelIndex &index, const bool compressed);
|
||||||
|
void setCompression(const UModelIndex &index, const UINT8 compression);
|
||||||
void setMarking(const UModelIndex &index, const UINT8 marking);
|
void setMarking(const UModelIndex &index, const UINT8 marking);
|
||||||
|
|
||||||
UINT32 offset(const UModelIndex &index) const;
|
UINT32 offset(const UModelIndex &index) const;
|
||||||
@ -168,6 +169,7 @@ public:
|
|||||||
bool hasEmptyTail(const UModelIndex &index) const;
|
bool hasEmptyTail(const UModelIndex &index) const;
|
||||||
bool fixed(const UModelIndex &index) const;
|
bool fixed(const UModelIndex &index) const;
|
||||||
bool compressed(const UModelIndex &index) const;
|
bool compressed(const UModelIndex &index) const;
|
||||||
|
UINT8 compression(const UModelIndex &index) const;
|
||||||
UINT8 marking(const UModelIndex &index) const;
|
UINT8 marking(const UModelIndex &index) const;
|
||||||
UINT8 action(const UModelIndex &index) const;
|
UINT8 action(const UModelIndex &index) const;
|
||||||
|
|
||||||
|
@ -460,8 +460,7 @@ USTATUS compress(const UByteArray & data, const UINT8 algorithm, UByteArray & co
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
//msg(usprintf("compress: unknown compression algorithm %1").arg(algorithm));
|
//msg(usprintf("compress: unknown compression algorithm %1").arg(algorithm));
|
||||||
//return U_UNKNOWN_COMPRESSION_ALGORITHM;
|
return U_UNKNOWN_COMPRESSION_ALGORITHM;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user