2 Commits

4 changed files with 382 additions and 50 deletions
+92
View File
@@ -0,0 +1,92 @@
# reHDD Code Style - clang-format configuration
# Based on analyzed codebase formatting
BasedOnStyle: LLVM
# Indentation
IndentWidth: 4
TabWidth: 4
UseTab: Never
ContinuationIndentWidth: 4
# Braces
BreakBeforeBraces: Allman
# Allman style:
# if (condition)
# {
# statement;
# }
# Spacing
SpaceAfterCStyleCast: false
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
# Alignment
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: false
# Line breaks
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
# Column limit
ColumnLimit: 0
# 0 = no limit (observed in the code)
# Breaking
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: false
# Pointers and references
PointerAlignment: Left
# Type *variable (not Type* variable)
# Include sorting
SortIncludes: false
# Keep includes in original order
# Comments
ReflowComments: false
# Don't reformat comments
# Access modifiers
AccessModifierOffset: -4
# public: is outdented
# Constructor initializers
ConstructorInitializerIndentWidth: 6
# Observed 6 spaces for initializer lists
# Other
Cpp11BracedListStyle: true
FixNamespaceComments: true
IncludeBlocks: Preserve
IndentCaseLabels: false
IndentPPDirectives: None
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PenaltyBreakBeforeFirstCallParameter: 100
PenaltyBreakComment: 300
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
+48 -4
View File
@@ -16,9 +16,28 @@
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <chrono>
#define CHUNK_SIZE 1024 * 1024 * 32 // amount of bytes that are overwritten at once --> 32MB
#define TFNG_DATA_SIZE CHUNK_SIZE // amount of bytes used by tfng
// Adaptive chunk size optimization - uncomment to enable
#define ADAPTIVE_CHUNK_SIZE
// Chunk size configuration
#define CHUNK_SIZE_START 1024 * 1024 * 32 // Starting chunk size: 32MB
#define CHUNK_SIZE_MIN 1024 * 1024 * 4 // Minimum chunk size: 4MB
#define CHUNK_SIZE_MAX 1024 * 1024 * 128 // Maximum chunk size: 128MB
#define CHUNK_SIZE_STEP_UP 1024 * 1024 * 2 // Increase step: 2MB
#define CHUNK_SIZE_STEP_DOWN 1024 * 1024 * 4 // Decrease step: 4MB
#define CHUNK_MEASURE_INTERVAL 64 // Measure performance every 64 chunks
#ifdef ADAPTIVE_CHUNK_SIZE
// Use max buffer size when adaptive mode is enabled
#define CHUNK_SIZE CHUNK_SIZE_MAX
#define TFNG_DATA_SIZE CHUNK_SIZE_MAX
#else
// Use fixed chunk size when adaptive mode is disabled
#define CHUNK_SIZE CHUNK_SIZE_START
#define TFNG_DATA_SIZE CHUNK_SIZE
#endif
// #define DEMO_DRIVE_SIZE 1024*1024*256L // 256MB
// #define DEMO_DRIVE_SIZE 1024*1024*1024L // 1GB
@@ -33,22 +52,47 @@ protected:
public:
Shred();
~Shred();
int shredDrive(Drive *drive, int *ipSignalFd);
int shredDrive(Drive* drive, int* ipSignalFd);
private:
fileDescriptor randomSrcFileDiscr;
fileDescriptor driveFileDiscr;
#ifdef ADAPTIVE_CHUNK_SIZE
unsigned char* caTfngData; // Dynamic buffer allocation for adaptive mode
unsigned char* caReadBuffer; // Dynamic buffer allocation for adaptive mode
#else
unsigned char caTfngData[TFNG_DATA_SIZE];
unsigned char caReadBuffer[CHUNK_SIZE];
#endif
unsigned long ulDriveByteSize;
unsigned long ulDriveByteOverallCount = 0; // all bytes shredded in all iterations + checking -> used for progress calculation
double d32Percent = 0.0;
double d32TmpPercent = 0.0;
#ifdef ADAPTIVE_CHUNK_SIZE
// Adaptive chunk size optimization members
size_t currentChunkSize;
size_t bestChunkSize;
unsigned int chunkCounter;
std::chrono::high_resolution_clock::time_point measurementStartTime;
double bestThroughputMBps;
double lastThroughputMBps;
unsigned long bytesWrittenInMeasurement;
bool throughputIncreasing;
// Adaptive methods
void startMeasurement();
void evaluateThroughput(Drive* drive);
void adjustChunkSize(Drive* drive);
size_t getCurrentChunkSize() const;
#endif
inline double calcProgress();
int iRewindDrive(fileDescriptor file);
long getDriveSizeInBytes(fileDescriptor file);
unsigned int uiCalcChecksum(fileDescriptor file, Drive *drive, int *ipSignalFd);
unsigned int uiCalcChecksum(fileDescriptor file, Drive* drive, int* ipSignalFd);
void cleanup();
};
+9 -9
View File
@@ -49,7 +49,7 @@ public:
static void initTUI();
void updateTUI(std::list<Drive> *plistDrives, uint8_t u8SelectedEntry);
void updateTUI(std::list<Drive>* plistDrives, uint8_t u8SelectedEntry);
static enum UserInput readUserInput();
@@ -60,12 +60,12 @@ private:
static std::string sRamUsage;
static std::string sLocalTime;
WINDOW *overview;
WINDOW *systemview;
WINDOW *detailview;
WINDOW *menuview;
WINDOW *dialog;
WINDOW *smartWarning;
WINDOW* overview;
WINDOW* systemview;
WINDOW* detailview;
WINDOW* menuview;
WINDOW* dialog;
WINDOW* smartWarning;
static void centerTitle(WINDOW *pwin, const char *title);
static WINDOW *createOverViewWindow(int iXSize, int iYSize);
@@ -79,9 +79,9 @@ private:
static WINDOW *createSmartWarning(int iXSize, int iYSize, int iXStart, int iYStart, std::string sPath, uint32_t u32PowerOnHours, uint32_t u32PowerCycles, uint32_t u32ErrorCount, uint32_t u32Temperature, uint32_t u32ReallocatedSectors, uint32_t u32PendingSectors, uint32_t u32UncorrectableSectors);
static WINDOW *createZeroChecksumWarning(int iXSize, int iYSize, int iXStart, int iYStart, std::string sPath, std::string sModelFamily, std::string sModelName, std::string sSerial, uint32_t u32Checksum);
void displaySelectedDrive(Drive &drive, int stdscrX, int stdscrY);
void displaySelectedDrive(Drive& drive, int stdscrX, int stdscrY);
std::string formatTimeDuration(time_t u32Duration);
std::string formatSpeed(time_t u32ShredTimeDelta, unsigned long ulWrittenBytes);
static void vTruncateText(std::string *psText, uint16_t u16MaxLenght);
static void vTruncateText(std::string* psText, uint16_t u16MaxLenght);
};
#endif // TUI_H_
+232 -36
View File
@@ -17,14 +17,64 @@ extern "C"
}
#endif
const static char *randomsrc = (char *)"/dev/urandom";
const static char* randomsrc = (char*)"/dev/urandom";
Shred::Shred()
{
#ifdef ADAPTIVE_CHUNK_SIZE
// Allocate aligned buffers for maximum chunk size
if (posix_memalign((void**)&caTfngData, 4096, CHUNK_SIZE_MAX) != 0)
{
Logger::logThis()->error("Failed to allocate aligned buffer for tfng data");
caTfngData = nullptr;
}
if (posix_memalign((void**)&caReadBuffer, 4096, CHUNK_SIZE_MAX) != 0)
{
Logger::logThis()->error("Failed to allocate aligned buffer for read buffer");
caReadBuffer = nullptr;
}
// Initialize adaptive tracking variables
currentChunkSize = CHUNK_SIZE_START;
bestChunkSize = CHUNK_SIZE_START;
chunkCounter = 0;
bestThroughputMBps = 0.0;
lastThroughputMBps = 0.0;
bytesWrittenInMeasurement = 0;
throughputIncreasing = true;
Logger::logThis()->info("Adaptive chunk size optimization ENABLED - Starting with " +
to_string(currentChunkSize / (1024 * 1024)) + " MB chunks");
#endif
}
Shred::~Shred()
{
#ifdef ADAPTIVE_CHUNK_SIZE
if (caTfngData != nullptr)
{
free(caTfngData);
caTfngData = nullptr;
}
if (caReadBuffer != nullptr)
{
free(caReadBuffer);
caReadBuffer = nullptr;
}
#endif
}
#ifdef ADAPTIVE_CHUNK_SIZE
/**
* \brief Start performance measurement interval
* \return void
*/
void Shred::startMeasurement()
{
measurementStartTime = std::chrono::high_resolution_clock::now();
bytesWrittenInMeasurement = 0;
chunkCounter = 0;
}
/**
@@ -33,10 +83,117 @@ Shred::~Shred()
* \param file descriptor for signaling
* \return 0 on success, -1 on error
*/
int Shred::shredDrive(Drive *drive, int *ipSignalFd)
void Shred::evaluateThroughput(Drive* drive)
{
auto measurementEndTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = measurementEndTime - measurementStartTime;
double elapsedSeconds = elapsed.count();
if (elapsedSeconds > 0.0)
{
double throughputMBps = (bytesWrittenInMeasurement / (1024.0 * 1024.0)) / elapsedSeconds;
lastThroughputMBps = throughputMBps;
Logger::logThis()->info("Throughput measurement - ChunkSize: " +
to_string(currentChunkSize / (1024 * 1024)) + " MB, " +
"Throughput: " + to_string((int)throughputMBps) + " MB/s, " +
"Best: " + to_string((int)bestThroughputMBps) + " MB/s" +
" - Drive: " + drive->getSerial());
// Check if this is better than our best
if (throughputMBps > bestThroughputMBps)
{
bestThroughputMBps = throughputMBps;
bestChunkSize = currentChunkSize;
throughputIncreasing = true;
Logger::logThis()->info("NEW BEST throughput: " + to_string((int)bestThroughputMBps) +
" MB/s with " + to_string(currentChunkSize / (1024 * 1024)) +
" MB chunks - Drive: " + drive->getSerial());
}
else
{
throughputIncreasing = false;
}
}
// Adjust chunk size for next measurement interval
adjustChunkSize(drive);
// Start new measurement
startMeasurement();
}
/**
* \brief Adjust chunk size based on throughput trend
* \param pointer to Drive instance
* \return void
*/
void Shred::adjustChunkSize(Drive* drive)
{
size_t oldChunkSize = currentChunkSize;
if (throughputIncreasing)
{
// Throughput is improving - increase chunk size
currentChunkSize += CHUNK_SIZE_STEP_UP;
// Clamp to maximum
if (currentChunkSize > CHUNK_SIZE_MAX)
{
currentChunkSize = CHUNK_SIZE_MAX;
Logger::logThis()->info("Reached maximum chunk size: " +
to_string(currentChunkSize / (1024 * 1024)) + " MB" +
" - Drive: " + drive->getSerial());
}
}
else
{
// Throughput decreased - decrease chunk size to find sweet spot
if (currentChunkSize > CHUNK_SIZE_STEP_DOWN)
{
currentChunkSize -= CHUNK_SIZE_STEP_DOWN;
}
// Clamp to minimum
if (currentChunkSize < CHUNK_SIZE_MIN)
{
currentChunkSize = CHUNK_SIZE_MIN;
Logger::logThis()->info("Reached minimum chunk size: " +
to_string(currentChunkSize / (1024 * 1024)) + " MB" +
" - Drive: " + drive->getSerial());
}
}
if (oldChunkSize != currentChunkSize)
{
Logger::logThis()->info("Adjusted chunk size: " +
to_string(oldChunkSize / (1024 * 1024)) + " MB -> " +
to_string(currentChunkSize / (1024 * 1024)) + " MB" +
" - Drive: " + drive->getSerial());
}
}
/**
* \brief Get current chunk size for adaptive mode
* \return current chunk size in bytes
*/
size_t Shred::getCurrentChunkSize() const
{
return currentChunkSize;
}
#endif
/**
* \brief shred drive with shred
* \param pointer of Drive instance
* \param file descriptor for signaling
* \return 0 on success, -1 on error
*/
int Shred::shredDrive(Drive* drive, int* ipSignalFd)
{
ostringstream address;
address << (void const *)&(*drive);
address << (void const*)&(*drive);
Logger::logThis()->info("Shred-Task started - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str());
// Mark as started but NOT shredded yet
@@ -72,10 +229,18 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd)
#endif
#ifndef DRYRUN
string sDrivePath = drive->getPath();
const char *cpDrivePath = sDrivePath.c_str();
const char* cpDrivePath = drive->getPath().c_str();
unsigned char ucKey[TFNG_KEY_SIZE];
#ifdef ADAPTIVE_CHUNK_SIZE
// Validate buffers were allocated
if (caTfngData == nullptr || caReadBuffer == nullptr)
{
Logger::logThis()->error("Shred-Task: Aligned buffers not allocated! - Drive: " + drive->getSerial());
return -1;
}
#endif
// Open random source
Logger::logThis()->info("Shred-Task: Opening random source: " + string(randomsrc) + " - Drive: " + drive->getSerial());
randomSrcFileDiscr = open(randomsrc, O_RDONLY | O_LARGEFILE);
@@ -182,14 +347,18 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd)
}
Drive::ShredSpeed shredSpeed = drive->sShredSpeed.load();
shredSpeed.chronoShredTimestamp = std::chrono::system_clock::now(); // set inital timestamp for speed metric
shredSpeed.ulSpeedMetricBytesWritten = 0U; // uses to calculate speed metric
shredSpeed.chronoShredTimestamp = std::chrono::system_clock::now();
shredSpeed.ulSpeedMetricBytesWritten = 0U;
drive->sShredSpeed.store(shredSpeed);
#ifdef LOG_LEVEL_HIGH
Logger::logThis()->info("Shred-Task: Bytes-Size of Drive: " + to_string(this->ulDriveByteSize) + " - Drive: " + drive->getSerial());
#endif
#ifdef ADAPTIVE_CHUNK_SIZE
// Start first measurement interval
startMeasurement();
#endif
// Main shredding loop
for (unsigned int uiShredIterationCounter = 0U; uiShredIterationCounter < SHRED_ITERATIONS; uiShredIterationCounter++)
{
@@ -200,44 +369,39 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd)
if (uiShredIterationCounter == (SHRED_ITERATIONS - 1))
{
// last shred iteration --> overwrite (just the write chunk) bytes with zeros instead with random data
#ifdef ADAPTIVE_CHUNK_SIZE
memset(caTfngData, 0U, CHUNK_SIZE_MAX);
#else
memset(caTfngData, 0U, CHUNK_SIZE);
#endif
}
while (ulDriveByteCounter < ulDriveByteSize)
{
// Check if task was aborted
if (drive->state.load() != Drive::TaskState::SHRED_ACTIVE)
{
Logger::logThis()->info("Shred-Task: Aborted by user at " + to_string(d32Percent) +
"% in iteration " + to_string(uiShredIterationCounter + 1) +
" - Drive: " + drive->getSerial());
drive->setTaskPercentage(0);
d32Percent = 0.00;
d32TmpPercent = 0.00;
cleanup();
// CRITICAL: Mark as NOT shredded on abort
drive->state = Drive::TaskState::NONE;
drive->bWasShredded = false;
drive->bWasChecked = false;
return -1;
}
#ifdef ADAPTIVE_CHUNK_SIZE
size_t activeChunkSize = getCurrentChunkSize();
#else
size_t activeChunkSize = CHUNK_SIZE;
#endif
int iBytesToShred = 0;
if (uiShredIterationCounter != (SHRED_ITERATIONS - 1))
{
// Generate random data for this chunk
#ifdef ADAPTIVE_CHUNK_SIZE
tfng_prng_genrandom(caTfngData, activeChunkSize);
#else
tfng_prng_genrandom(caTfngData, TFNG_DATA_SIZE);
#endif
}
if ((ulDriveByteSize - ulDriveByteCounter) < CHUNK_SIZE)
if ((ulDriveByteSize - ulDriveByteCounter) < activeChunkSize)
{
iBytesToShred = (ulDriveByteSize - ulDriveByteCounter);
}
else
{
iBytesToShred = CHUNK_SIZE;
iBytesToShred = activeChunkSize;
}
int iByteShredded = write(driveFileDiscr, caTfngData, iBytesToShred);
@@ -267,7 +431,20 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd)
ulDriveByteCounter += iByteShredded;
ulDriveByteOverallCount += iByteShredded;
#ifdef ADAPTIVE_CHUNK_SIZE
bytesWrittenInMeasurement += iByteShredded;
chunkCounter++;
// Evaluate throughput after measurement interval
if (chunkCounter >= CHUNK_MEASURE_INTERVAL)
{
evaluateThroughput(drive);
}
#endif
d32Percent = this->calcProgress();
#ifdef LOG_LEVEL_HIGH
Logger::logThis()->info("Shred-Task: ByteCount: " + to_string(ulDriveByteCounter) +
" - iteration: " + to_string((uiShredIterationCounter + 1)) +
@@ -277,12 +454,21 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd)
if ((d32Percent - d32TmpPercent) >= 0.01)
{
// set shred percantage
drive->setTaskPercentage(d32TmpPercent);
d32TmpPercent = d32Percent;
// signal process in shredding
write(*ipSignalFd, "A", 1);
}
if (drive->state != Drive::TaskState::SHRED_ACTIVE)
{
drive->setTaskPercentage(0);
d32Percent = 0.00;
d32TmpPercent = 0.00;
ulDriveByteCounter = 0U;
Logger::logThis()->info("Aborted shred for: " + drive->getModelName() + "-" + drive->getSerial());
cleanup();
return -1;
}
}
Logger::logThis()->info("Shred-Task: Iteration " + to_string(uiShredIterationCounter + 1) + "/" +
@@ -304,12 +490,19 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd)
}
}
#ifdef ADAPTIVE_CHUNK_SIZE
Logger::logThis()->info("Shred completed - Optimal chunk size: " +
to_string(bestChunkSize / (1024 * 1024)) + " MB, " +
"Best throughput: " + to_string((int)bestThroughputMBps) + " MB/s" +
" - Drive: " + drive->getSerial());
#endif
// All shred iterations completed successfully
tfng_prng_seedkey(NULL);
// ONLY mark as shredded if ALL iterations completed AND fsync succeeded
drive->bWasShredded = true;
Logger::logThis()->info("Shred-Task finished successfully - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str());
Logger::logThis()->info("Shred-Task finished - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str());
#ifdef ZERO_CHECK
drive->state = Drive::TaskState::CHECK_ACTIVE;
@@ -369,7 +562,7 @@ double Shred::calcProgress()
unsigned int uiMaxShredIteration = SHRED_ITERATIONS;
#ifdef ZERO_CHECK
uiMaxShredIteration++; // increment because we will check after SHRED_ITERATIONS the drive for non-zero bytes
uiMaxShredIteration++;
#endif
if (this->ulDriveByteSize == 0)
@@ -442,12 +635,16 @@ long Shred::getDriveSizeInBytes(fileDescriptor file)
* \param signal file descriptor
* \return checksum value (0 = all zeros)
*/
unsigned int Shred::uiCalcChecksum(fileDescriptor file, Drive *drive, int *ipSignalFd)
unsigned int Shred::uiCalcChecksum(fileDescriptor file, Drive* drive, int* ipSignalFd)
{
unsigned int uiChecksum = 0;
unsigned long ulDriveByteCounter = 0U;
Logger::logThis()->info("Check-Task: Starting checksum verification - Drive: " + drive->getSerial());
#ifdef ADAPTIVE_CHUNK_SIZE
size_t checkChunkSize = CHUNK_SIZE_MAX;
#else
size_t checkChunkSize = CHUNK_SIZE;
#endif
while (ulDriveByteCounter < ulDriveByteSize)
{
@@ -459,14 +656,13 @@ unsigned int Shred::uiCalcChecksum(fileDescriptor file, Drive *drive, int *ipSig
}
int iBytesToCheck = 0;
if ((ulDriveByteSize - ulDriveByteCounter) < CHUNK_SIZE)
if ((ulDriveByteSize - ulDriveByteCounter) < checkChunkSize)
{
iBytesToCheck = (ulDriveByteSize - ulDriveByteCounter);
}
else
{
iBytesToCheck = CHUNK_SIZE;
iBytesToCheck = checkChunkSize;
}
int iReadBytes = read(file, caReadBuffer, iBytesToCheck);