/** * Purges test devices/volumes prior to testing. Purge methods are determined * by the type and capabilities of the target devices/volumes and the runtime * arguments --nosecureerase, --notrim and --nozerofill. Returns TRUE on * success, FALSE otherwise. Purge methods are tracked on a per device basis * using the instance variable $purgeMethods * @return boolean */ public final function purge() { $purgeCount = 0; $nopurge = isset($this->options['nopurge']) && $this->options['nopurge']; $nosecureerase = isset($this->options['nosecureerase']) && $this->options['nosecureerase']; $notrim = isset($this->options['notrim']) && $this->options['notrim']; $nozerofill = isset($this->options['nozerofill']) && $this->options['nozerofill']; if (!$nopurge) { foreach ($this->options['target'] as $target) { $purged = FALSE; $volume = BlockStorageTest::getVolume($target); $rotational = BlockStorageTest::isRotational($target); print_msg(sprintf('Attempting to purge %srotational target %s with --nosecureerase=%d; --notrim=%d; --nozerofill=%d', $rotational ? '' : 'non-', $target, $nosecureerase ? '1' : '0', $notrim ? '1' : '0', $nozerofill ? '1' : '0'), $this->verbose, __FILE__, __LINE__); // try ATA secure erase if ($this->deviceTargets && !$nosecureerase) { print_msg(sprintf('Attempting ATA secure erase for target %s', $target), $this->verbose, __FILE__, __LINE__); $cmd = sprintf('hdparm --user-master u --security-erase "%s" %s >/dev/null 2>&1; echo $?', $this->options['secureerase_pswd'], $target); $ecode = trim(exec($cmd)); if ($ecode > 0) { print_msg(sprintf('ATA secure erase not supported or failed for target %s', $target), $this->verbose, __FILE__, __LINE__); } else { print_msg(sprintf('ATA secure erase successful for target %s', $target), $this->verbose, __FILE__, __LINE__); $this->purgeMethods[$target] = 'secureerase'; $purged = TRUE; } } else { print_msg(sprintf('ATA secure erase not be attempted for %s because %s', $target, $nosecureerase ? '--nosecureerase argument was specified (or implied due to lack of --secureerase_pswd argument)' : 'it is not a device'), $this->verbose, __FILE__, __LINE__); } // next try TRIM // if (!$purged && !$rotational && !$notrim) { if (!$purged && !$notrim) { $cmd = sprintf(($this->deviceTargets ? 'blkdiscard' : 'fstrim') . '%s %s >/dev/null 2>&1; echo $?', $this->deviceTargets && isset($this->options['trim_offset_end']) && $this->options['trim_offset_end'] > 0 ? sprintf(' -o 0 -l %d', BlockStorageTest::getFreeSpace($target, TRUE) - $this->options['trim_offset_end']) : '', $this->deviceTargets ? $target : $volume); print_msg(sprintf('Attempting TRIM for volume %s using command %s', $volume, $cmd), $this->verbose, __FILE__, __LINE__); $ecode = trim(exec($cmd)); if ($ecode > 0) { print_msg(sprintf('TRIM not supported or failed for target %s (exit code %d)', $target, $ecode), $this->verbose, __FILE__, __LINE__); } else { print_msg(sprintf('TRIM successful for target %s', $target), $this->verbose, __FILE__, __LINE__); $this->purgeMethods[$target] = 'trim'; $purged = TRUE; } } else { if (!$purged) { print_msg(sprintf('TRIM not attempted for target %s because %s', $target, $notrim ? '--notrim argument was specified' : 'device is rotational'), $this->verbose, __FILE__, __LINE__); } } // finally try zero filling if (!$purged && !$nozerofill) { $size = BlockStorageTest::getFreeSpace($target); // adjust for active range and volume target free space buffer if ($this->options['active_range'] < 100) { $size *= $this->options['active_range'] * 0.01; } else { if ($this->volumeTargets) { $size -= BlockStorageTest::BLOCK_STORAGE_TEST_FREE_SPACE_BUFFER; } } $size = round($size); if ($size < 1) { print_msg(sprintf('Target %s does not have sufficient space (%d MB) to accomodate free space buffer (%d MB)', $target, $size + BlockStorageTest::BLOCK_STORAGE_TEST_FREE_SPACE_BUFFER, BlockStorageTest::BLOCK_STORAGE_TEST_FREE_SPACE_BUFFER), $this->verbose, __FILE__, __LINE__, TRUE); } else { print_msg(sprintf('Attempting to zero fill target %s with %d MB. This may take a while...', $target, $size), $this->verbose, __FILE__, __LINE__); $cmd = sprintf('dd if=/dev/zero of=%s bs=1M count=%d >/dev/null 2>&1; echo $?', $file = $target . ($this->volumeTargets ? '/' . BlockStorageTest::BLOCK_STORAGE_TEST_FILE_NAME : ''), $size); $ecode = trim(exec($cmd)); // delete zero file from volume type targets if ($this->volumeTargets) { print_msg(sprintf('Removing temporary zero fill file %s', $file), $this->verbose, __FILE__, __LINE__); exec(sprintf('rm -f %s', $file)); } if ($ecode > 0) { print_msg(sprintf('Zero fill failed for target %s (exit code %d)', $target, $ecode), $this->verbose, __FILE__, __LINE__); } else { print_msg(sprintf('Zero fill successful for target %s', $target), $this->verbose, __FILE__, __LINE__); $this->purgeMethods[$target] = 'zero'; $purged = TRUE; } } } else { if (!$purged) { print_msg(sprintf('Zero fill not attempted for target %s because %s', $target, $nozerofill ? '--nozerofill argument was specified' : 'it is not a device'), $this->verbose, __FILE__, __LINE__); } } if ($purged) { print_msg(sprintf('Target %s purged successfully using %s', $target, $this->purgeMethods[$target]), $this->verbose, __FILE__, __LINE__); $purgeCount++; } else { print_msg(sprintf('Target %s could not be purged', $target), $this->verbose, __FILE__, __LINE__); } } } return $purgeCount == count($this->options['target']); }