/** * @file shred.cpp * @brief shred drive * @author hendrik schutter * @date 22.08.2022 */ #include "../include/reHDD.h" #ifdef __cplusplus extern "C" { #endif #include "../tfnoisegen/tfprng.h" #ifdef __cplusplus } #endif const static char *randomsrc = (char *)"/dev/urandom"; Shred::Shred() { } Shred::~Shred() { } /** * \brief shred drive with shred * \param pointer of Drive instance * \return void */ 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()); #ifdef DRYRUN for (int i = 0; i <= 500; i++) { if (drive->state != Drive::SHRED_ACTIVE) { return 0; } drive->setTaskPercentage(i + 0.05); write(*ipSignalFd, "A", 1); usleep(20000); } #endif #ifndef DRYRUN const char *cpDrivePath = drive->getPath().c_str(); unsigned char ucKey[TFNG_KEY_SIZE]; // open random source randomSrcFileDiscr = open(randomsrc, O_RDONLY | O_LARGEFILE); if (randomSrcFileDiscr == -1) { std::string errorMsg(strerror(randomSrcFileDiscr)); Logger::logThis()->error("Shred-Task: Open random source failed! " + errorMsg + " - Drive: " + drive->getSerial()); perror(randomsrc); cleanup(); return -1; } // open disk driveFileDiscr = open(cpDrivePath, O_RDWR | O_LARGEFILE); if (driveFileDiscr == -1) { std::string errorMsg(strerror(driveFileDiscr)); Logger::logThis()->error("Shred-Task: Open drive failed! " + errorMsg + " - Drive: " + drive->getSerial()); perror(cpDrivePath); cleanup(); return -1; } // read key for random generator ssize_t readRet = read(randomSrcFileDiscr, ucKey, sizeof(ucKey)); if (readRet <= 0) { std::string errorMsg(strerror(readRet)); Logger::logThis()->error("Shred-Task: Read random key failed! " + errorMsg + " - Drive: " + drive->getSerial()); perror(randomsrc); cleanup(); return -1; } tfng_prng_seedkey(ucKey); this->ulDriveByteSize = getDriveSizeInBytes(driveFileDiscr); drive->sShredSpeed.chronoShredTimestamp = std::chrono::system_clock::now(); ; // set inital timestamp for speed metric drive->sShredSpeed.ulSpeedMetricBytesWritten = 0U; // uses to calculate speed metric #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task: Bytes-Size of Drive: " + to_string(this->ulDriveByteSize) + " - Drive: " + drive->getSerial()); #endif 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 if (uiShredIterationCounter == (SHRED_ITERATIONS - 1)) { // last shred iteration --> overwrite (just the write chunk) bytes with zeros instead with random data memset(caTfngData, 0U, CHUNK_SIZE); } while (ulDriveByteCounter < ulDriveByteSize) { int iBytesToShred = 0; // Bytes that will be overwritten in this chunk-iteration if (uiShredIterationCounter != (SHRED_ITERATIONS - 1)) { // NOT last shred iteration --> generate new random data tfng_prng_genrandom(caTfngData, TFNG_DATA_SIZE); } if ((ulDriveByteSize - ulDriveByteCounter) < CHUNK_SIZE) { iBytesToShred = (ulDriveByteSize - ulDriveByteCounter); } else { iBytesToShred = CHUNK_SIZE; } int iByteShredded = write(driveFileDiscr, caTfngData, iBytesToShred); if (iByteShredded <= 0) { std::string errorMsg(strerror(iByteShredded)); Logger::logThis()->error("Shred-Task: Write to drive failed! " + errorMsg + " - Drive: " + drive->getSerial()); perror("unable to write random data"); cleanup(); return -1; } ulDriveByteCounter += iByteShredded; ulDriveByteOverallCount += iByteShredded; d32Percent = this->calcProgress(); drive->sShredSpeed.ulSpeedMetricBytesWritten += iByteShredded; #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()); #endif if ((d32Percent - d32TmpPercent) >= 0.01) { // set shred percantage drive->setTaskPercentage(d32TmpPercent); d32TmpPercent = d32Percent; // signal process in shreding write(*ipSignalFd, "A", 1); } if (drive->state != Drive::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 } if (0 != iRewindDrive(driveFileDiscr)) { Logger::logThis()->error("Shred-Task: Unable to rewind drive! - Drive: " + drive->getSerial()); cleanup(); return -1; } // end one shred iteration } // end of all shred iteratio tfng_prng_seedkey(NULL); // reset random generator drive->bWasShredded = true; Logger::logThis()->info("Shred-Task finished - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str()); #ifdef ZERO_CHECK drive->state = Drive::CHECK_ACTIVE; Logger::logThis()->info("Check-Task started - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str()); drive->u32DriveChecksumAfterShredding = uiCalcChecksum(driveFileDiscr, drive, ipSignalFd); #ifdef LOG_LEVEL_HIGH if (drive->u32DriveChecksumAferShredding != 0) { Logger::logThis()->info("Shred-Task: Checksum not zero: " + to_string(drive->u32DriveChecksumAfterShredding) + " - Drive: " + drive->getSerial()); } else { Logger::logThis()->info("Shred-Task: Checksum zero: " + to_string(drive->u32DriveChecksumAfterShredding) + " - Drive: " + drive->getSerial()); } #endif #endif #endif cleanup(); if ((drive->state == Drive::SHRED_ACTIVE) || (drive->state == Drive::CHECK_ACTIVE)) { drive->state = Drive::NONE; drive->setTaskPercentage(0.0); Printer::getPrinter()->print(drive); Logger::logThis()->info("Finished 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() { unsigned int uiMaxShredIteration = SHRED_ITERATIONS; #ifdef ZERO_CHECK uiMaxShredIteration++; // increment because we will check after SHRED_ITERATIONS the drive for non-zero bytes #endif return (double)(((double)ulDriveByteOverallCount) / ((double)this->ulDriveByteSize * uiMaxShredIteration)) * 100.0f; } int Shred::iRewindDrive(fileDescriptor file) { if (0 != lseek(file, 0L, SEEK_SET)) { perror("unable to rewind drive"); return -1; } else { return 0; } } unsigned long Shred::getDriveSizeInBytes(fileDescriptor file) { unsigned long ulDriveSizeTmp = lseek(file, 0L, SEEK_END); if (0 != iRewindDrive(file)) { ulDriveSizeTmp = 0U; } #ifdef DEMO_DRIVE_SIZE ulDriveSizeTmp = DEMO_DRIVE_SIZE; #endif return ulDriveSizeTmp; } unsigned int Shred::uiCalcChecksum(fileDescriptor file, Drive *drive, int *ipSignalFd) { unsigned int uiChecksum = 0; unsigned long ulDriveByteCounter = 0U; while (ulDriveByteCounter < ulDriveByteSize) { int iBytesToCheck = 0; if ((ulDriveByteSize - ulDriveByteCounter) < CHUNK_SIZE) { iBytesToCheck = (ulDriveByteSize - ulDriveByteCounter); } else { iBytesToCheck = CHUNK_SIZE; } int iReadBytes = read(file, caReadBuffer, iBytesToCheck); for (int iReadBytesCounter = 0U; iReadBytesCounter < iReadBytes; iReadBytesCounter++) { uiChecksum += caReadBuffer[iReadBytesCounter]; } ulDriveByteCounter += iReadBytes; ulDriveByteOverallCount += iReadBytes; d32Percent = this->calcProgress(); drive->sShredSpeed.ulSpeedMetricBytesWritten += iReadBytes; #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task (Checksum): ByteCount: " + to_string(ulDriveByteCounter) + " - progress: " + to_string(d32Percent) + " - Drive: " + drive->getSerial()); #endif if (((d32Percent - d32TmpPercent) >= 0.01) || (d32Percent == 100.0)) { drive->setTaskPercentage(d32TmpPercent); d32TmpPercent = d32Percent; #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("send progress signal to main loop (check)"); #endif write(*ipSignalFd, "A", 1); } } drive->bWasChecked = true; return uiChecksum; } void Shred::cleanup() { close(driveFileDiscr); close(randomSrcFileDiscr); }