From e08624161598076d0bcdf3656eb997ea0ad7809a Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 26 Apr 2026 21:16:23 +0200 Subject: [PATCH 1/2] update version --- include/reHDD.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/reHDD.h b/include/reHDD.h index 40f93e4..aaad511 100644 --- a/include/reHDD.h +++ b/include/reHDD.h @@ -8,7 +8,7 @@ #ifndef REHDD_H_ #define REHDD_H_ -#define REHDD_VERSION "V1.3.0" +#define REHDD_VERSION "V1.3.1" // Drive handling Settings #define WORSE_HOURS 19200 // mark drive if at this limit or beyond -- 2.54.0 From 5e50c5f489fb0e3056b7e6a09cfaba977bf3aa54 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 26 Apr 2026 21:55:35 +0200 Subject: [PATCH 2/2] fix error handling --- src/shred.cpp | 337 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 274 insertions(+), 63 deletions(-) diff --git a/src/shred.cpp b/src/shred.cpp index 6cddd6c..b8f9abb 100644 --- a/src/shred.cpp +++ b/src/shred.cpp @@ -29,16 +29,20 @@ Shred::~Shred() /** * \brief shred drive with shred - * \param pointer of Drive instance - * \return void + * \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); Logger::logThis()->info("Shred-Task started - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str()); - drive->bWasShredStarted = true; // Mark drive as partly shredded + + // Mark as started but NOT shredded yet + drive->bWasShredStarted = true; drive->bWasShredded = false; + drive->bWasChecked = false; drive->setTaskPercentage(0.0); drive->u32DriveChecksumAfterShredding = UINT32_MAX; drive->state = Drive::TaskState::SHRED_ACTIVE; @@ -46,53 +50,136 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd) #ifdef DRYRUN for (int i = 0; i <= 100; i++) { - drive->setTaskPercentage(i + 0.05); + if (drive->state.load() != Drive::TaskState::SHRED_ACTIVE) + { + Logger::logThis()->info("Shred-Task aborted during DRYRUN - Drive: " + drive->getSerial()); + drive->setTaskPercentage(i + 0.05); + drive->state = Drive::TaskState::NONE; + drive->bWasShredded = false; // CRITICAL: Mark as NOT shredded on abort + return -1; + } + drive->setTaskPercentage((double)i); write(*ipSignalFd, "A", 1); usleep(20000); } + + // Only mark as shredded if DRYRUN completed successfully drive->bWasShredded = true; + drive->setTaskPercentage(0.0); + drive->state = Drive::TaskState::NONE; + Logger::logThis()->info("DRYRUN completed - Drive: " + drive->getSerial()); + return 0; #endif #ifndef DRYRUN const char *cpDrivePath = drive->getPath().c_str(); unsigned char ucKey[TFNG_KEY_SIZE]; - // open random source + // Open random source + Logger::logThis()->info("Shred-Task: Opening random source: " + string(randomsrc) + " - Drive: " + drive->getSerial()); randomSrcFileDiscr = open(randomsrc, O_RDONLY | O_LARGEFILE); if (randomSrcFileDiscr == -1) { - std::string errorMsg(strerror(errno)); - Logger::logThis()->error("Shred-Task: Open random source failed! " + errorMsg + " - Drive: " + drive->getSerial()); - perror(randomsrc); - cleanup(); + int savedErrno = errno; + Logger::logThis()->error("Shred-Task: Open random source failed! Path: " + string(randomsrc) + + " - Error: " + strerror(savedErrno) + " (errno: " + to_string(savedErrno) + ")" + + " - Drive: " + drive->getSerial()); + + // Reset drive state on error - NOT shredded + drive->state = Drive::TaskState::NONE; + drive->setTaskPercentage(0.0); + drive->bWasShredStarted = false; + drive->bWasShredded = false; return -1; } + Logger::logThis()->info("Shred-Task: Random source opened successfully (fd: " + to_string(randomSrcFileDiscr) + ") - Drive: " + drive->getSerial()); - // open disk + // Open disk driveFileDiscr = open(cpDrivePath, O_RDWR | O_LARGEFILE); if (driveFileDiscr == -1) { - std::string errorMsg(strerror(errno)); - Logger::logThis()->error("Shred-Task: Open drive failed! " + errorMsg + " - Drive: " + drive->getSerial()); - perror(cpDrivePath); - cleanup(); + int savedErrno = errno; + string errorDetail; + + switch (savedErrno) + { + case ENOMEDIUM: + errorDetail = "No medium found (drive may be empty or disconnected)"; + break; + case EACCES: + errorDetail = "Permission denied (need root/sudo?)"; + break; + case ENOENT: + errorDetail = "Drive not found (device may have been removed)"; + break; + case EROFS: + errorDetail = "Read-only file system"; + break; + case EBUSY: + errorDetail = "Drive is busy (may be mounted or in use)"; + break; + case EINVAL: + errorDetail = "Invalid argument"; + break; + default: + errorDetail = strerror(savedErrno); + break; + } + + Logger::logThis()->error("Shred-Task: Open drive failed! Path: " + string(cpDrivePath) + + " - Error: " + errorDetail + " (errno: " + to_string(savedErrno) + ")" + + " - Drive: " + drive->getSerial() + " - Model: " + drive->getModelName()); + + // Close random source before returning + close(randomSrcFileDiscr); + randomSrcFileDiscr = -1; + + // Reset drive state on error - NOT shredded + drive->state = Drive::TaskState::NONE; + drive->setTaskPercentage(0.0); + drive->bWasShredStarted = false; + drive->bWasShredded = false; return -1; } + Logger::logThis()->info("Shred-Task: Drive opened successfully (fd: " + to_string(driveFileDiscr) + ") - Drive: " + drive->getSerial()); - // read key for random generator + // Read key for random generator + Logger::logThis()->info("Shred-Task: Reading random key - Drive: " + drive->getSerial()); ssize_t readRet = read(randomSrcFileDiscr, ucKey, sizeof(ucKey)); if (readRet <= 0) { - std::string errorMsg(strerror(errno)); - Logger::logThis()->error("Shred-Task: Read random key failed! " + errorMsg + " - Drive: " + drive->getSerial()); - perror(randomsrc); + int savedErrno = errno; + Logger::logThis()->error("Shred-Task: Read random key failed! Expected: " + to_string(sizeof(ucKey)) + + " bytes, Got: " + to_string(readRet) + " bytes" + + " - Error: " + strerror(savedErrno) + " (errno: " + to_string(savedErrno) + ")" + + " - Drive: " + drive->getSerial()); cleanup(); + + // Reset drive state on error - NOT shredded + drive->state = Drive::TaskState::NONE; + drive->setTaskPercentage(0.0); + drive->bWasShredStarted = false; + drive->bWasShredded = false; return -1; } + Logger::logThis()->info("Shred-Task: Random key read successfully (" + to_string(readRet) + " bytes) - Drive: " + drive->getSerial()); tfng_prng_seedkey(ucKey); this->ulDriveByteSize = getDriveSizeInBytes(driveFileDiscr); + if (this->ulDriveByteSize == 0) + { + Logger::logThis()->error("Shred-Task: Drive size is 0 bytes! Drive may be empty or size detection failed - Drive: " + drive->getSerial()); + cleanup(); + + // Reset drive state on error - NOT shredded + drive->state = Drive::TaskState::NONE; + drive->setTaskPercentage(0.0); + drive->bWasShredStarted = false; + drive->bWasShredded = false; + return -1; + } + 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 @@ -102,9 +189,12 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd) Logger::logThis()->info("Shred-Task: Bytes-Size of Drive: " + to_string(this->ulDriveByteSize) + " - Drive: " + drive->getSerial()); #endif + // Main shredding loop for (unsigned int uiShredIterationCounter = 0U; uiShredIterationCounter < SHRED_ITERATIONS; uiShredIterationCounter++) { - unsigned long ulDriveByteCounter = 0U; // used for one shred-iteration to keep track of the current drive position + // Logger::logThis()->info("Shred-Task: Starting iteration " + to_string(uiShredIterationCounter + 1) + "/" + to_string(SHRED_ITERATIONS) + " - Drive: " + drive->getSerial()); + + unsigned long ulDriveByteCounter = 0U; if (uiShredIterationCounter == (SHRED_ITERATIONS - 1)) { @@ -114,11 +204,29 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd) while (ulDriveByteCounter < ulDriveByteSize) { - int iBytesToShred = 0; // Bytes that will be overwritten in this chunk-iteration + // 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; + } + + int iBytesToShred = 0; if (uiShredIterationCounter != (SHRED_ITERATIONS - 1)) { - // NOT last shred iteration --> generate new random data + // Generate random data for this chunk tfng_prng_genrandom(caTfngData, TFNG_DATA_SIZE); } @@ -135,10 +243,20 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd) if (iByteShredded <= 0) { - std::string errorMsg(strerror(errno)); - Logger::logThis()->error("Shred-Task: Write to drive failed! " + errorMsg + " - Drive: " + drive->getSerial()); - perror("unable to write random data"); + int savedErrno = errno; + Logger::logThis()->error("Shred-Task: Write to drive failed! Attempted: " + to_string(iBytesToShred) + + " bytes, Written: " + to_string(iByteShredded) + " bytes" + + " - Position: " + to_string(ulDriveByteCounter) + "/" + to_string(ulDriveByteSize) + + " - Iteration: " + to_string(uiShredIterationCounter + 1) + "/" + to_string(SHRED_ITERATIONS) + + " - Error: " + strerror(savedErrno) + " (errno: " + to_string(savedErrno) + ")" + + " - Drive: " + drive->getSerial()); cleanup(); + + // CRITICAL: Mark as NOT shredded on write failure + drive->state = Drive::TaskState::NONE; + drive->setTaskPercentage(0.0); + drive->bWasShredded = false; + drive->bWasChecked = false; return -1; } @@ -150,7 +268,10 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd) ulDriveByteOverallCount += iByteShredded; d32Percent = this->calcProgress(); #ifdef LOG_LEVEL_HIGH - Logger::logThis()->info("Shred-Task: ByteCount: " + to_string(ulDriveByteCounter) + " - iteration: " + to_string((uiShredIterationCounter + 1)) + " - progress: " + to_string(d32Percent) + " - Drive: " + drive->getSerial()); + Logger::logThis()->info("Shred-Task: ByteCount: " + to_string(ulDriveByteCounter) + + " - iteration: " + to_string((uiShredIterationCounter + 1)) + + " - progress: " + to_string(d32Percent) + "%" + + " - Drive: " + drive->getSerial()); #endif if ((d32Percent - d32TmpPercent) >= 0.01) @@ -158,36 +279,37 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd) // set shred percantage drive->setTaskPercentage(d32TmpPercent); d32TmpPercent = d32Percent; - // signal process in shreding + // 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; - } - // end one chunk write } + + Logger::logThis()->info("Shred-Task: Iteration " + to_string(uiShredIterationCounter + 1) + "/" + + to_string(SHRED_ITERATIONS) + " completed - Drive: " + drive->getSerial()); + + // Rewind drive for next iteration if (0 != iRewindDrive(driveFileDiscr)) { - Logger::logThis()->error("Shred-Task: Unable to rewind drive! - Drive: " + drive->getSerial()); + Logger::logThis()->error("Shred-Task: Unable to rewind drive after iteration " + + to_string(uiShredIterationCounter + 1) + " - Drive: " + drive->getSerial()); cleanup(); + + // CRITICAL: Mark as NOT shredded on rewind failure + drive->state = Drive::TaskState::NONE; + drive->setTaskPercentage(0.0); + drive->bWasShredded = false; + drive->bWasChecked = false; return -1; } - // end one shred iteration } - // end of all shred iteratio - tfng_prng_seedkey(NULL); // reset random generator + // 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 - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str()); + Logger::logThis()->info("Shred-Task finished successfully - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str()); + #ifdef ZERO_CHECK drive->state = Drive::TaskState::CHECK_ACTIVE; Logger::logThis()->info("Check-Task started - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str()); @@ -196,33 +318,49 @@ int Shred::shredDrive(Drive *drive, int *ipSignalFd) if (drive->u32DriveChecksumAfterShredding != 0) { drive->state = Drive::TaskState::CHECK_FAILED; - Logger::logThis()->info("Shred-Task: Checksum not zero: " + to_string(drive->u32DriveChecksumAfterShredding) + " - Drive: " + drive->getSerial()); + Logger::logThis()->error("Check-Task: Checksum verification failed! Expected: 0, Got: " + + to_string(drive->u32DriveChecksumAfterShredding) + " - Drive: " + drive->getSerial()); } else { drive->state = Drive::TaskState::CHECK_SUCCESSFUL; - Logger::logThis()->info("Shred-Task: Checksum zero: " + to_string(drive->u32DriveChecksumAfterShredding) + " - Drive: " + drive->getSerial()); + drive->bWasChecked = true; + Logger::logThis()->info("Check-Task: Checksum verification passed (zero) - Drive: " + drive->getSerial()); } #endif + cleanup(); #endif - if ((drive->state.load() == Drive::TaskState::SHRED_ACTIVE) || (drive->state.load() == Drive::TaskState::CHECK_SUCCESSFUL) || (drive->state == Drive::TaskState::CHECK_FAILED)) + // Final state handling - ONLY process if shred actually completed + Drive::TaskState finalState = drive->state.load(); + + // Only do final processing if we reached a completion state + // (not if we returned early with errors) + if ((finalState == Drive::TaskState::SHRED_ACTIVE) || + (finalState == Drive::TaskState::CHECK_SUCCESSFUL) || + (finalState == Drive::TaskState::CHECK_FAILED)) { - if (drive->state != Drive::TaskState::CHECK_FAILED) + if (finalState != Drive::TaskState::CHECK_FAILED) { + Logger::logThis()->info("Shred-Task: Triggering print for drive - Drive: " + drive->getSerial()); Printer::getPrinter()->print(drive); } + else + { + Logger::logThis()->warning("Shred-Task: Skipping print due to checksum failure - Drive: " + drive->getSerial()); + } + drive->state = Drive::TaskState::NONE; drive->setTaskPercentage(0.0); - Logger::logThis()->info("Finished shred/check for: " + drive->getModelName() + "-" + drive->getSerial()); + Logger::logThis()->info("Completed shred/check for: " + drive->getModelName() + "-" + drive->getSerial()); } + return 0; } + /** * \brief calc shredding progress in % - * \param current byte index of the drive - * \param current shred iteration * \return double percentage */ double Shred::calcProgress() @@ -232,54 +370,95 @@ double Shred::calcProgress() #ifdef ZERO_CHECK uiMaxShredIteration++; // increment because we will check after SHRED_ITERATIONS the drive for non-zero bytes #endif + if (this->ulDriveByteSize == 0) return 0.0; - return (double)(((double)ulDriveByteOverallCount) / ((double)this->ulDriveByteSize * uiMaxShredIteration)) * 100.0f; + + return (double)(((double)ulDriveByteOverallCount) / ((double)this->ulDriveByteSize * uiMaxShredIteration)) * 100.0; } +/** + * \brief rewind drive to beginning + * \param file descriptor + * \return 0 on success, -1 on error + */ int Shred::iRewindDrive(fileDescriptor file) { - if (0 != lseek(file, 0L, SEEK_SET)) + off_t result = lseek(file, 0L, SEEK_SET); + + if (result == -1) { - perror("unable to rewind drive"); - Logger::logThis()->info("Unable to rewind drive! - fileDescriptor: " + to_string(file)); + int savedErrno = errno; + Logger::logThis()->error("Unable to rewind drive! Error: " + string(strerror(savedErrno)) + + " (errno: " + to_string(savedErrno) + ") - fileDescriptor: " + to_string(file)); return -1; } - else + else if (result != 0) { - return 0; + Logger::logThis()->error("Rewind position mismatch! Expected: 0, Got: " + to_string(result) + + " - fileDescriptor: " + to_string(file)); + return -1; } + + return 0; } +/** + * \brief get drive size in bytes + * \param file descriptor + * \return size in bytes, 0 on error + */ long Shred::getDriveSizeInBytes(fileDescriptor file) { - long liDriveSizeTmp = lseek(file, 0L, SEEK_END); + off_t liDriveSizeTmp = lseek(file, 0L, SEEK_END); if (liDriveSizeTmp == -1) { - perror("unable to get drive size"); - Logger::logThis()->info("Unable to get drive size! - fileDescriptor: " + to_string(file)); + int savedErrno = errno; + Logger::logThis()->error("Unable to get drive size! Error: " + string(strerror(savedErrno)) + + " (errno: " + to_string(savedErrno) + ") - fileDescriptor: " + to_string(file)); return 0L; } if (0 != iRewindDrive(file)) { - liDriveSizeTmp = 0L; + Logger::logThis()->error("Unable to rewind after size detection - fileDescriptor: " + to_string(file)); + return 0L; } #ifdef DEMO_DRIVE_SIZE liDriveSizeTmp = DEMO_DRIVE_SIZE; + Logger::logThis()->info("DEMO_DRIVE_SIZE active - using size: " + to_string(liDriveSizeTmp) + " bytes"); #endif + return liDriveSizeTmp; } +/** + * \brief calculate checksum of drive (verify all zeros) + * \param file descriptor + * \param pointer to Drive instance + * \param signal file descriptor + * \return checksum value (0 = all zeros) + */ 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()); + while (ulDriveByteCounter < ulDriveByteSize) { + // Check if task was aborted + if (drive->state.load() != Drive::TaskState::CHECK_ACTIVE) + { + Logger::logThis()->info("Check-Task: Aborted by user at " + to_string(d32Percent) + "% - Drive: " + drive->getSerial()); + return UINT32_MAX; // Return non-zero to indicate incomplete check + } + int iBytesToCheck = 0; + if ((ulDriveByteSize - ulDriveByteCounter) < CHUNK_SIZE) { iBytesToCheck = (ulDriveByteSize - ulDriveByteCounter); @@ -289,6 +468,18 @@ unsigned int Shred::uiCalcChecksum(fileDescriptor file, Drive *drive, int *ipSig iBytesToCheck = CHUNK_SIZE; } int iReadBytes = read(file, caReadBuffer, iBytesToCheck); + + if (iReadBytes <= 0) + { + int savedErrno = errno; + Logger::logThis()->error("Check-Task: Read failed! Attempted: " + to_string(iBytesToCheck) + + " bytes, Read: " + to_string(iReadBytes) + " bytes" + + " - Position: " + to_string(ulDriveByteCounter) + "/" + to_string(ulDriveByteSize) + + " - Error: " + strerror(savedErrno) + " (errno: " + to_string(savedErrno) + ")" + + " - Drive: " + drive->getSerial()); + return UINT32_MAX; // Return non-zero to indicate read failure + } + for (int iReadBytesCounter = 0U; iReadBytesCounter < iReadBytes; iReadBytesCounter++) { uiChecksum += caReadBuffer[iReadBytesCounter]; @@ -301,7 +492,10 @@ unsigned int Shred::uiCalcChecksum(fileDescriptor file, Drive *drive, int *ipSig drive->sShredSpeed.store(shredSpeed); #ifdef LOG_LEVEL_HIGH - Logger::logThis()->info("Shred-Task (Checksum): ByteCount: " + to_string(ulDriveByteCounter) + " - progress: " + to_string(d32Percent) + " - Drive: " + drive->getSerial()); + Logger::logThis()->info("Check-Task: ByteCount: " + to_string(ulDriveByteCounter) + + " - progress: " + to_string(d32Percent) + "%" + + " - checksum so far: " + to_string(uiChecksum) + + " - Drive: " + drive->getSerial()); #endif if (((d32Percent - d32TmpPercent) >= 0.01) || (d32Percent == 100.0)) @@ -314,12 +508,29 @@ unsigned int Shred::uiCalcChecksum(fileDescriptor file, Drive *drive, int *ipSig write(*ipSignalFd, "A", 1); } } + + Logger::logThis()->info("Check-Task: Verification complete - Final checksum: " + to_string(uiChecksum) + " - Drive: " + drive->getSerial()); drive->bWasChecked = true; + return uiChecksum; } +/** + * \brief cleanup - close file descriptors + */ void Shred::cleanup() { - close(driveFileDiscr); - close(randomSrcFileDiscr); + if (driveFileDiscr != -1) + { + Logger::logThis()->info("Shred-Task: Closing drive file descriptor: " + to_string(driveFileDiscr)); + close(driveFileDiscr); + driveFileDiscr = -1; + } + + if (randomSrcFileDiscr != -1) + { + Logger::logThis()->info("Shred-Task: Closing random source file descriptor: " + to_string(randomSrcFileDiscr)); + close(randomSrcFileDiscr); + randomSrcFileDiscr = -1; + } } \ No newline at end of file -- 2.54.0