/* Copyright (C) 2023 Mike Fleetwood
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
/* Test GParted_Core::erase_filesystem_signatures()
*/
#include "common.h"
#include "insertion_operators.h"
#include "GParted_Core.h"
#include "OperationDetail.h"
#include "Partition.h"
#include "Utils.h"
#include "gtest/gtest.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace GParted
{
// Explicit test fixture class for common variables and methods used in each test.
// Reference:
// Google Test, Primer, Test Fixtures: Using the Same Data Configuration for Multiple Tests
class EraseFileSystemSignaturesTest : public ::testing::Test
{
protected:
virtual void create_image_file(Byte_Value size);
virtual void write_signatures(const char* signature, const std::vector& sector_offsets);
virtual void write_intel_software_raid_signature();
virtual void write_all_possible_promise_fasttrack_raid_signatures();
virtual bool image_contains_all_zeros();
virtual void TearDown();
bool erase_filesystem_signatures(const Partition& partition, OperationDetail& operationdetail)
{ return m_gparted_core.erase_filesystem_signatures(partition, operationdetail); };
static const char* s_image_name;
GParted_Core m_gparted_core;
Partition m_partition;
OperationDetail m_operation_detail;
};
const char* EraseFileSystemSignaturesTest::s_image_name = "test_EraseFileSystemSignatures.img";
void EraseFileSystemSignaturesTest::create_image_file(Byte_Value size)
{
// Create new image file to work with.
unlink(s_image_name);
int fd = open(s_image_name, O_WRONLY|O_CREAT|O_NONBLOCK, 0666);
ASSERT_GE(fd, 0) << "Failed to create image file '" << s_image_name << "'. errno="
<< errno << "," << strerror(errno);
ASSERT_EQ(ftruncate(fd, (off_t)size), 0) << "Failed to set image file '" << s_image_name << "' to size "
<< size << ". errno=" << errno << "," << strerror(errno);
close(fd);
// Initialise m_partition as a Partition object spanning the whole of the image file.
m_partition.Reset();
PedDevice* lp_device = ped_device_get(s_image_name);
ASSERT_TRUE(lp_device != NULL);
m_partition.set_unpartitioned(s_image_name,
lp_device->path,
FS_UNALLOCATED,
lp_device->length,
lp_device->sector_size,
false);
ped_device_destroy(lp_device);
lp_device = NULL;
}
void EraseFileSystemSignaturesTest::write_signatures(const char* signature, const std::vector& sector_offsets)
{
const off_t SectorSize = 512;
int fd = open(s_image_name, O_WRONLY|O_NONBLOCK);
ASSERT_GE(fd, 0) << "Failed to open image file '" << s_image_name << "'. errno="
<< errno << "," << strerror(errno);
size_t signature_len = strlen(signature);
for (size_t i = 0; i < sector_offsets.size(); i++)
{
// Positive offsets are relative to the start of the file, negative
// offsets relative to the end of the file.
int whence = sector_offsets[i] >= 0 ? SEEK_SET : SEEK_END;
ASSERT_GE(lseek(fd, sector_offsets[i] * SectorSize, whence), 0)
<< "Failed to seek in image file '" << s_image_name << "'. errno="
<< errno << "," << strerror(errno);
ASSERT_EQ(write(fd, signature, signature_len), (ssize_t)signature_len)
<< "Failed to write to image file '" << s_image_name << "'. errno="
<< errno << "," << strerror(errno);
}
close(fd);
}
void EraseFileSystemSignaturesTest::write_intel_software_raid_signature()
{
// Write Intel Software RAID signature at -2 sectors before the end. Hard codes
// sector size to 512 bytes for a file.
// Reference:
// .../util-linux/libblkid/src/superblocks/isw_raid.c:probe_iswraid().
std::vector sector_offsets{-2};
write_signatures("Intel Raid ISM Cfg Sig. ", sector_offsets);
}
void EraseFileSystemSignaturesTest::write_all_possible_promise_fasttrack_raid_signatures()
{
// Write all possible Promise FastTrack RAID signatures.
// Reference:
// .../util-linux/libblkid/src/superblocks/promise_raid.c:probe_pdcraid().
std::vector sector_offsets{-63, -255, -256, -16, -399, -591, -675, -735, -911, -974, -991, -951, -3087};
write_signatures("Promise Technology, Inc.", sector_offsets);
}
const char* first_non_zero_byte(const char* buf, size_t size)
{
while (size > 0)
{
if (*buf != '\0')
return buf;
buf++;
size--;
}
return NULL;
}
bool EraseFileSystemSignaturesTest::image_contains_all_zeros()
{
int fd = open(s_image_name, O_RDONLY|O_NONBLOCK);
if (fd < 0)
{
ADD_FAILURE() << __func__ << "(): Failed to open image file '" << s_image_name << "'. errno="
<< errno << "," << strerror(errno);
return false;
}
ssize_t bytes_read = 0;
size_t offset = 0;
do
{
char buf[BUFSIZ];
bytes_read = read(fd, buf, sizeof(buf));
if (bytes_read < 0)
{
ADD_FAILURE() << __func__ << "(): Failed to read from image file '" << s_image_name
<< "'. errno=" << errno << "," << strerror(errno);
close(fd);
return false;
}
const char* p = first_non_zero_byte(buf, bytes_read);
if (p != NULL)
{
ADD_FAILURE() << __func__ << "(): First non-zero bytes:\n"
<< binary_string_to_print(offset + (p - buf), p, buf + bytes_read - p);
close(fd);
return false;
}
}
while (bytes_read > 0);
close(fd);
return true;
}
void EraseFileSystemSignaturesTest::TearDown()
{
unlink(s_image_name);
}
TEST_F(EraseFileSystemSignaturesTest, IntelSoftwareRAIDAligned)
{
create_image_file(16 * MEBIBYTE);
write_intel_software_raid_signature();
EXPECT_TRUE(erase_filesystem_signatures(m_partition, m_operation_detail)) << m_operation_detail;
EXPECT_TRUE(image_contains_all_zeros());
}
TEST_F(EraseFileSystemSignaturesTest, IntelSoftwareRAIDUnaligned)
{
create_image_file(16 * MEBIBYTE - 512);
write_intel_software_raid_signature();
EXPECT_TRUE(erase_filesystem_signatures(m_partition, m_operation_detail)) << m_operation_detail;
EXPECT_TRUE(image_contains_all_zeros());
}
TEST_F(EraseFileSystemSignaturesTest, PromiseFastTrackRAIDAligned)
{
create_image_file(16 * MEBIBYTE);
write_all_possible_promise_fasttrack_raid_signatures();
EXPECT_TRUE(erase_filesystem_signatures(m_partition, m_operation_detail)) << m_operation_detail;
EXPECT_TRUE(image_contains_all_zeros());
}
TEST_F(EraseFileSystemSignaturesTest, PromiseFastTrackRAIDUnaligned)
{
create_image_file(16 * MEBIBYTE - 512);
write_all_possible_promise_fasttrack_raid_signatures();
EXPECT_TRUE(erase_filesystem_signatures(m_partition, m_operation_detail)) << m_operation_detail;
EXPECT_TRUE(image_contains_all_zeros());
}
} // namespace GParted
// Custom Google Test main().
// Reference:
// * Google Test, Primer, Writing the main() function
// https://github.com/google/googletest/blob/master/googletest/docs/primer.md#writing-the-main-function
int main(int argc, char** argv)
{
printf("Running main() from %s\n", __FILE__);
GParted::ensure_x11_display(argc, argv);
// Initialise threading in GParted to successfully use Utils:: and
// FileSystem::execute_command(). Must be before InitGoogleTest().
GParted::GParted_Core::mainthread = Glib::Thread::self();
Gtk::Main gtk_main = Gtk::Main();
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}