Tool zum Dauerstresstest für HDDs/SSDs

Anoubis

Admiral
Registriert
März 2010
Beiträge
7.384
Hallo zusammen,

ich suche ein Tool mit GUI, das einen Datenträger endlos beschreibt: Also Vollschreiben, löschen, wieder vollschreiben usw.
Vor einigen Jahren hatte ich bereits so ein Program im Einsatz, aber ich kann es nicht mehr finden.
Hat jemand eine Idee?
 
Für lineares Beschreiben (ohne viel Kopfbewegung) bieten sich diverse "LowLevelFormat"-Tools an.

Für größere mechanische Belastung bieten sich diverse Benchmarks in Dauerschleife an.

Ich würde vermutlich auf die extended Tests des Herstellereigenen Programms zurückgreifen. Da hat man Stresstest und Fehlerauswertung in einem.
 
Camarillo schrieb:
Tool H2testw würde sich anbieten
Bist Du Dir sicher, dass H2testw seinen Kriterien entspricht?🤔 Insbesondere:
Anoubis schrieb:
Datenträger endlos beschreibt: Also Vollschreiben, löschen, wieder vollschreiben usw.
@Anoubis was nu, für SSD oder HDD? Es wäre einfacher ein Tool zu empfehlen, wenn man versteht, was Du damit bezwecken willst.
 
@Anoubis
Keine Ahnung was du damit verfolgst, ich vermute fast ein XY-Problem.

Es gibt doch Tools wie Darik's Boot and Nuke ("DBAN"), womit man eine Festplatte sicher formatieren kann, mit x-facher Überschreibung des Datenträgers.

Oder einfach nach "secure eraser" (oder ähnliches) oder nach "Peter Gutmann" googeln. Letzteres ist eine Formatierungsmethode, bei dem der Datenträger 35 Mal überschrieben wird.

Oder erfüllt das deine Anforderung von "vollschreiben" nicht? Es werden dabei keine Dateien angelegt, sondern die Datenträger werden auf Sektor-Ebene vollgeschrieben.
 
  • Gefällt mir
Reaktionen: iSight2TheBlind, Johhai und ugul
Klingt für mich auch nach XY-Problem.
Vielleicht will der TE auch nur Daten vernichten durch das endlose überschreiben. (Wäre es hier nicht sinnvoller, überschriebene SSD/NVME einfach physikalisch zu zerstören bzw. bei HDDs diese geöffnet in Salzwasser zu legen und nicht mehr heraus zu nehmen?)
DBAN wäre auch meine Empfehlung gewesen.
Falls du SSDs oder NVME löschen willst gibt es andere programme bzw. BIOS. Du kannst auch eine komplette schicht überschreiben (1 Schreibzyklus) (aber k.a. ob dann auch reservesektoren vernichtet werden).
 
massaker schrieb:
Bist Du Dir sicher, dass H2testw seinen Kriterien entspricht?
Da bin ich sogar absolut sicher.

H2testw.png
 
  • Gefällt mir
Reaktionen: cartridge_case und kieleich
fsutil file createnew D:\test.amiga 1073741824

erstellt eine Datei, die eine Größe von 1 GB hat. Ergänze diese und erstelle dir ein Script....
 
Anoubis schrieb:
Also Vollschreiben, löschen, wieder vollschreiben
Was ist für dich der Unterschied zwischen vollschreiben und löschen ? Löschen ist Endeffekt doch nur Überschreiben mit Nullen, Vollschreiben Überschreiben mit einem anderen Muster als Nullen.
Und brauchst du eventeuell auch eine Abbruchbedingung ? Z.B Wenn das gelesene Muster nicht mit dem übereinstimmt, was geschrieben wurde ?
 
1. Mehrfach überschreiben aus datenschutzgründen ist schwachsiiiiinnnnnnn!
2. Reduziert es bei SSD die Lebensdauer
3. Reduziert es bei HDD die Lebendsdauer (Workload beachten)

selbst bei uralten 500 GB HDDs ist die Chance (nach dem 1xigen überschreiben) für das richtige wiederherstellen eines einzelnen Bit bei ca 56%. Für 1 Byte müsste man 8x richtig liegen.

Die Empfehlung HDDs mehrmals zu überschreiben stammt aus MFM-Festplatten und Diskettenzeiten! Aber die Hersteller vermarkten es immer noch so als wäre es heute immer noch wichtig.

https://www.heise.de/news/Sicheres-Loeschen-Einmal-ueberschreiben-genuegt-198816.html
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Fusionator und Azghul0815
Interessant was sich hier einige zusammenreimen :rolleyes:
Kein XY-Problem, nix Datenschutz :freak:
Es geht um einen Dauerstresstest.
Danke an @Camarillo! :)
 
Wieso ein UI Tool? Ein kleines PS Script kann das auch erledigen.

PowerShell:
# HDD Stress Test – Laufwerk komplett vollschreiben, dann löschen, repeat
$path = "C:\Temp\stresstest.tmp"
$chunkSize = 100MB
$buffer = New-Object byte[] $chunkSize
(New-Object Random).NextBytes($buffer)

while ($true) {
    $drive = Split-Path -Qualifier $path
    $free = (Get-PSDrive ($drive.TrimEnd(':'))).Free
    Write-Host "$(Get-Date -Format 'HH:mm:ss') – Verfügbar: $([Math]::Round($free/1GB,2)) GB – Starte Schreibvorgang..."

    $stream = [System.IO.File]::OpenWrite($path)
    $written = 0
    try {
        while ($true) {
            $stream.Write($buffer, 0, $buffer.Length)
            $written += $chunkSize
            Write-Host -NoNewline "`r  Geschrieben: $([Math]::Round($written/1GB,2)) GB"
        }
    }
    catch [System.IO.IOException] {
        Write-Host "`n$(Get-Date -Format 'HH:mm:ss') – Laufwerk voll ($([Math]::Round($written/1GB,2)) GB geschrieben)"
    }
    finally {
        $stream.Close()
    }

    Write-Host "$(Get-Date -Format 'HH:mm:ss') – Lösche Datei..."
    Remove-Item $path -Force
    Write-Host "$(Get-Date -Format 'HH:mm:ss') – Nächste Runde...`n"
}
 
  • Gefällt mir
Reaktionen: Azghul0815
Anoubis schrieb:
Es geht um einen Dauerstresstest.
Für HDDs ok, bedenke aber, dass sich der Flashspeicher von SSDs beim schreiben/löschen tatsächlich physikalisch abnutzt. In der normalen alltäglichen Nutzung ist das mittlerweile kein Problem (weil ja auch nicht permanent viele Daten geschrieben werden), Lasttest wo in kürzester Zeit TB-weise Daten auf die Platte geballert werden würde ich aber eher vermeiden oder zumindest möglichst kurz halten und nicht stundenlang durchlaufen lassen, ansonsten kann das die Lebenszeit der SSD verkürzen.
 
Anoubis schrieb:
Es geht um einen Dauerstresstest.
Ok,. gut, dann bitte kläre uns doch auf, warum?
Immerhin sind wir im TecForum und alle auch interessiert, wieso jemand (du) das machst, zumindest ich. Du machst das ja nicht aus purer Freude, vermute ich mal.

Alternativ geht auch sowas:
Datei anlegen mit dem Namen:
fill-drive-loop.ps1
und dann den Code unten einfügen.
Spuckt ein log aus und funktioniert im Test gerade.

Code:
#requires -version 5.1

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$ErrorActionPreference = "Stop"

$script:DefaultBlockSizesMB = @(8, 32, 64, 128, 256)
$script:DefaultCountdownSeconds = 5

function Write-Section {
    param([string]$Text)
    Write-Host ""
    Write-Host ("=" * 72) -ForegroundColor DarkCyan
    Write-Host (" {0}" -f $Text) -ForegroundColor Cyan
    Write-Host ("=" * 72) -ForegroundColor DarkCyan
}

function Write-WarnLine {
    param([string]$Text)
    Write-Host $Text -ForegroundColor Yellow
}

function Write-ErrorLine {
    param([string]$Text)
    Write-Host $Text -ForegroundColor Red
}

function Write-InfoLine {
    param([string]$Text)
    Write-Host $Text -ForegroundColor Gray
}

function Read-MenuChoice {
    param(
        [string]$Prompt,
        [string[]]$AllowedValues
    )

    do {
        $value = Read-Host $Prompt
    } until ($AllowedValues -contains $value)

    return $value
}

function Read-PositiveInt {
    param([string]$Prompt)

    do {
        $value = Read-Host $Prompt
        $parsed = 0
        $ok = [int]::TryParse($value, [ref]$parsed)
    } until ($ok -and $parsed -gt 0)

    return $parsed
}

function Read-PositiveDouble {
    param([string]$Prompt)

    do {
        $value = Read-Host $Prompt
        $parsed = 0.0

        $ok = [double]::TryParse(
            $value,
            [System.Globalization.NumberStyles]::Float,
            [System.Globalization.CultureInfo]::InvariantCulture,
            [ref]$parsed
        )

        if (-not $ok) {
            $ok = [double]::TryParse(
                $value,
                [System.Globalization.NumberStyles]::Float,
                [System.Globalization.CultureInfo]::CurrentCulture,
                [ref]$parsed
            )
        }
    } until ($ok -and $parsed -gt 0)

    return $parsed
}

function Format-Bytes {
    param([Int64]$Bytes)

    if ($Bytes -ge 1TB) { return "{0:N2} TB" -f ($Bytes / 1TB) }
    if ($Bytes -ge 1GB) { return "{0:N2} GB" -f ($Bytes / 1GB) }
    if ($Bytes -ge 1MB) { return "{0:N2} MB" -f ($Bytes / 1MB) }
    if ($Bytes -ge 1KB) { return "{0:N2} KB" -f ($Bytes / 1KB) }
    return "$Bytes Bytes"
}

function New-TimeStampedName {
    param(
        [string]$Prefix = "disk_fill_test",
        [string]$Extension = ".tmp"
    )

    return "{0}_{1}{2}" -f $Prefix, (Get-Date -Format "yyyyMMdd_HHmmss"), $Extension
}

function Resolve-NormalizedPath {
    param([string]$Path)

    return [System.IO.Path]::GetFullPath($Path)
}

function Resolve-PathOnDrive {
    param(
        [string]$InputPath,
        [string]$DriveRoot
    )

    if ([string]::IsNullOrWhiteSpace($InputPath)) {
        return (Resolve-NormalizedPath -Path $DriveRoot)
    }

    if ([System.IO.Path]::IsPathRooted($InputPath)) {
        return (Resolve-NormalizedPath -Path $InputPath)
    }

    return (Resolve-NormalizedPath -Path (Join-Path $DriveRoot $InputPath))
}

function Test-PathOnDrive {
    param(
        [string]$Path,
        [string]$DriveRoot
    )

    $normalizedPath = Resolve-NormalizedPath -Path $Path
    $normalizedRoot = Resolve-NormalizedPath -Path $DriveRoot

    if (-not $normalizedRoot.EndsWith("\")) {
        $normalizedRoot += "\"
    }

    return $normalizedPath.StartsWith($normalizedRoot, [System.StringComparison]::OrdinalIgnoreCase)
}

function Get-VolumeMap {
    $result = @{}

    try {
        $partitions = Get-Partition -ErrorAction Stop
        foreach ($partition in $partitions) {
            if ([string]::IsNullOrWhiteSpace($partition.DriveLetter)) {
                continue
            }

            $driveKey = ("{0}:" -f $partition.DriveLetter.ToUpper())
            $disk = $null

            try {
                $disk = Get-Disk -Number $partition.DiskNumber -ErrorAction Stop
            }
            catch {
                $disk = $null
            }

            $result[$driveKey] = [PSCustomObject]@{
                DriveLetter = $driveKey
                DiskNumber  = $partition.DiskNumber
                Partition   = $partition
                Disk        = $disk
            }
        }
    }
    catch {
    }

    return $result
}

function Get-DriveInventory {
    $logicalDisks = Get-CimInstance Win32_LogicalDisk |
        Where-Object { $_.DriveType -in 2, 3 } |
        Sort-Object DeviceID

    $volumeMap = Get-VolumeMap
    $inventory = @()

    foreach ($ld in $logicalDisks) {
        $driveKey = $ld.DeviceID.ToUpper()
        $disk = $null
        $model = "-"
        $busType = "-"
        $mediaHint = "Unbekannt"
        $isSSD = $false

        if ($volumeMap.ContainsKey($driveKey) -and $volumeMap[$driveKey].Disk) {
            $disk = $volumeMap[$driveKey].Disk
            if ($disk.FriendlyName) { $model = [string]$disk.FriendlyName }
            if ($disk.BusType) { $busType = [string]$disk.BusType }
            if ($disk.MediaType) { $mediaHint = [string]$disk.MediaType }

            if (
                ([string]$disk.MediaType -match "SSD") -or
                ([string]$disk.FriendlyName -match "SSD") -or
                ([string]$disk.FriendlyName -match "NVMe") -or
                ([string]$disk.BusType -match "NVMe")
            ) {
                $isSSD = $true
            }
        }

        $inventory += [PSCustomObject]@{
            DeviceID   = $ld.DeviceID
            DriveType  = $ld.DriveType
            VolumeName = $ld.VolumeName
            FileSystem = $ld.FileSystem
            Size       = [Int64]$ld.Size
            FreeSpace  = [Int64]$ld.FreeSpace
            DriveRoot  = ($ld.DeviceID + "\")
            Model      = $model
            BusType    = $busType
            MediaHint  = $mediaHint
            IsSSD      = $isSSD
        }
    }

    return $inventory
}

function Select-Drive {
    $drives = Get-DriveInventory

    if (-not $drives -or $drives.Count -eq 0) {
        throw "Keine lokalen oder wechselbaren Laufwerke gefunden."
    }

    Write-Section "Laufwerk auswählen"

    for ($i = 0; $i -lt $drives.Count; $i++) {
        $d = $drives[$i]
        $label = if ($d.VolumeName) { $d.VolumeName } else { "-" }
        $type = if ($d.DriveType -eq 2) { "Wechselbar" } else { "Lokal" }
        $medium = if ($d.IsSSD) { "SSD/NVMe erkannt" } else { $d.MediaHint }

        Write-Host ("[{0}] {1} | Label: {2} | Frei: {3} | Gesamt: {4}" -f `
            ($i + 1), $d.DeviceID, $label, (Format-Bytes $d.FreeSpace), (Format-Bytes $d.Size)) -ForegroundColor Green
        Write-Host ("    Typ: {0} | Dateisystem: {1} | Bus: {2} | Medium: {3}" -f `
            $type, $d.FileSystem, $d.BusType, $medium) -ForegroundColor DarkGray
        Write-Host ("    Modell: {0}" -f $d.Model) -ForegroundColor DarkGray
    }

    do {
        $choice = Read-Host "`nNummer des Laufwerks wählen"
        $parsed = 0
        $valid = [int]::TryParse($choice, [ref]$parsed) -and $parsed -ge 1 -and $parsed -le $drives.Count
    } until ($valid)

    return $drives[$parsed - 1]
}

function Show-DriveWarnings {
    param($Drive)

    Write-Section "Hinweise"
    Write-WarnLine "Mit CTRL+C kann der Test jederzeit abgebrochen werden."
    Write-WarnLine "Dieses Skript dient einem Schreib-Stresstest, nicht sicherem Löschen."

    if ($Drive.IsSSD) {
        Write-WarnLine ("SSD/NVMe erkannt auf {0}. Häufige Vollschreib-Tests erzeugen zusätzliche Schreiblast." -f $Drive.DeviceID)
    }
    else {
        Write-InfoLine ("Medium auf {0}: {1}" -f $Drive.DeviceID, $Drive.MediaHint)
    }

    if ($Drive.DeviceID -ieq "C:") {
        Write-WarnLine "Du hast C: gewählt. Ein Vollschreiben des freien Platzes kann Windows oder laufende Prozesse stören."
    }
}

function Get-TargetFolder {
    param($Drive)

    Write-Section "Zielordner auswählen"
    Write-InfoLine ("Enter für Laufwerks-Root: {0}" -f $Drive.DriveRoot)

    $inputPath = Read-Host "Zielordner"
    $resolvedPath = Resolve-PathOnDrive -InputPath $inputPath -DriveRoot $Drive.DriveRoot

    if (-not (Test-Path $resolvedPath)) {
        $create = Read-MenuChoice -Prompt "Pfad existiert nicht. Erstellen? (j/n)" -AllowedValues @("j","J","n","N")
        if ($create -in @("j","J")) {
            New-Item -Path $resolvedPath -ItemType Directory -Force | Out-Null
        }
        else {
            throw "Zielordner existiert nicht."
        }
    }

    if (-not (Test-PathOnDrive -Path $resolvedPath -DriveRoot $Drive.DriveRoot)) {
        throw "Der Zielordner muss auf dem gewählten Laufwerk liegen."
    }

    return (Resolve-Path $resolvedPath).Path
}

function Get-TestFileName {
    Write-Section "Dateiname"

    $defaultName = New-TimeStampedName
    Write-InfoLine ("Standardname: {0}" -f $defaultName)

    $name = Read-Host "Dateiname, Enter für Standard"
    if ([string]::IsNullOrWhiteSpace($name)) {
        $name = $defaultName
    }

    if ($name.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -ge 0) {
        throw "Ungültiger Dateiname."
    }

    return $name
}

function Get-LogFileName {
    param([string]$TestFileName)

    $baseName = [System.IO.Path]::GetFileNameWithoutExtension($TestFileName)
    return ("{0}.log.txt" -f $baseName)
}

function Safe-WriteLog {
    param(
        [string]$LogPath,
        [string]$Message
    )

    if ([string]::IsNullOrWhiteSpace($LogPath)) {
        return
    }

    try {
        $line = "{0} | {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message
        Add-Content -Path $LogPath -Value $line -ErrorAction Stop
    }
    catch {
    }
}

function Initialize-Log {
    param([string]$LogPath)

    try {
        if (-not (Test-Path $LogPath)) {
            New-Item -Path $LogPath -ItemType File -Force | Out-Null
        }
    }
    catch {
    }
}

function Test-WritePermission {
    param([string]$FolderPath)

    $probeFile = Join-Path $FolderPath ("write_probe_{0}.tmp" -f ([guid]::NewGuid().ToString("N")))

    try {
        [System.IO.File]::WriteAllText($probeFile, "probe")
        Remove-Item $probeFile -Force -ErrorAction Stop
    }
    catch {
        throw "Schreibtest im Zielordner fehlgeschlagen: $($_.Exception.Message)"
    }
}

function Run-MiniWriteTest {
    param([string]$FolderPath)

    Write-Section "Kurzer Schreibtest"
    Write-InfoLine "Lege kleine Testdatei an und lösche sie wieder ..."

    $probeFile = Join-Path $FolderPath ("mini_write_test_{0}.tmp" -f ([guid]::NewGuid().ToString("N")))

    try {
        $bytes = New-Object byte[] (64KB)
        [System.IO.File]::WriteAllBytes($probeFile, $bytes)
        Remove-Item $probeFile -Force -ErrorAction Stop
        Write-Host "Schreibtest erfolgreich." -ForegroundColor Green
    }
    catch {
        throw "Kurzer Schreibtest fehlgeschlagen: $($_.Exception.Message)"
    }
}

function Get-SizeMode {
    Write-Section "Schreibmenge"
    Write-Host "[1] Maximal freier Platz, bis Laufwerk voll meldet" -ForegroundColor Green
    Write-Host "[2] Definierte Grösse in GB" -ForegroundColor Green

    return (Read-MenuChoice -Prompt "Bitte wählen" -AllowedValues @("1","2"))
}

function Get-FixedSizeBytes {
    $sizeGB = Read-PositiveDouble -Prompt "Gewünschte Grösse in GB"
    return [Int64]($sizeGB * 1GB)
}

function Get-LoopCount {
    Write-Section "Durchläufe"
    Write-Host "[1] 1 Durchlauf" -ForegroundColor Green
    Write-Host "[2] 2 Durchläufe" -ForegroundColor Green
    Write-Host "[3] 3 Durchläufe" -ForegroundColor Green
    Write-Host "[4] Benutzerdefiniert" -ForegroundColor Green
    Write-Host "[5] Unendlich bis CTRL+C" -ForegroundColor Green

    $choice = Read-MenuChoice -Prompt "Bitte wählen" -AllowedValues @("1","2","3","4","5")

    switch ($choice) {
        "1" { return 1 }
        "2" { return 2 }
        "3" { return 3 }
        "4" { return (Read-PositiveInt -Prompt "Anzahl Durchläufe") }
        "5" { return -1 }
    }
}

function Get-BlockSizeBytes {
    Write-Section "Blockgrösse"

    for ($i = 0; $i -lt $script:DefaultBlockSizesMB.Count; $i++) {
        $mb = $script:DefaultBlockSizesMB[$i]
        $suffix = if ($mb -eq 64) { " (Standard)" } else { "" }
        Write-Host ("[{0}] {1} MB{2}" -f ($i + 1), $mb, $suffix) -ForegroundColor Green
    }
    Write-Host ("[{0}] Benutzerdefiniert" -f ($script:DefaultBlockSizesMB.Count + 1)) -ForegroundColor Green

    $allowed = 1..($script:DefaultBlockSizesMB.Count + 1) | ForEach-Object { "$_" }
    $choice = Read-MenuChoice -Prompt "Bitte wählen" -AllowedValues $allowed

    if ([int]$choice -le $script:DefaultBlockSizesMB.Count) {
        $mb = $script:DefaultBlockSizesMB[[int]$choice - 1]
        return [Int64]($mb * 1MB)
    }

    $customMB = Read-PositiveInt -Prompt "Blockgrösse in MB"
    return [Int64]($customMB * 1MB)
}

function Get-CurrentFreeBytes {
    param([string]$DriveLetter)

    $current = Get-CimInstance Win32_LogicalDisk -Filter ("DeviceID='{0}'" -f $DriveLetter)
    if (-not $current) {
        throw "Laufwerk $DriveLetter konnte nicht erneut ausgelesen werden."
    }

    return [Int64]$current.FreeSpace
}

function Start-Countdown {
    param([int]$Seconds = 5)

    Write-Section "Start"
    for ($i = $Seconds; $i -ge 1; $i--) {
        Write-Host ("Start in {0} ..." -f $i) -ForegroundColor Yellow
        Start-Sleep -Seconds 1
    }
}

function Handle-ExistingFile {
    param([string]$FilePath)

    if (-not (Test-Path $FilePath)) {
        return
    }

    Write-Section "Vorhandene Testdatei gefunden"
    Write-WarnLine ("Die Datei existiert bereits: {0}" -f $FilePath)
    Write-Host "[1] Überschreiben" -ForegroundColor Green
    Write-Host "[2] Löschen und neu starten" -ForegroundColor Green
    Write-Host "[3] Abbrechen" -ForegroundColor Green

    $choice = Read-MenuChoice -Prompt "Bitte wählen" -AllowedValues @("1","2","3")

    switch ($choice) {
        "1" { return }
        "2" {
            Remove-Item $FilePath -Force -ErrorAction Stop
            return
        }
        "3" {
            throw "Abgebrochen, weil bereits eine Datei mit demselben Namen existiert."
        }
    }
}

function Confirm-SystemDriveIfNeeded {
    param([string]$DriveLetter)

    if ($DriveLetter -ieq "C:") {
        $confirm = Read-MenuChoice -Prompt "Du hast C: gewählt. Wirklich fortfahren? (j/n)" -AllowedValues @("j","J","n","N")
        if ($confirm -notin @("j","J")) {
            throw "Abgebrochen."
        }
    }
}

function Write-TestFileFixedSize {
    param(
        [string]$FilePath,
        [Int64]$TargetBytes,
        [Int64]$BlockSizeBytes,
        [string]$LogPath
    )

    $freeBytesNow = Get-CurrentFreeBytes -DriveLetter ([System.IO.Path]::GetPathRoot($FilePath).TrimEnd('\'))
    if ($TargetBytes -gt $freeBytesNow) {
        throw ("Zu wenig freier Platz. Frei: {0}, benötigt: {1}" -f `
            (Format-Bytes $freeBytesNow), (Format-Bytes $TargetBytes))
    }

    $buffer = New-Object byte[] $BlockSizeBytes
    for ($i = 0; $i -lt $buffer.Length; $i++) {
        $buffer[$i] = 0xAA
    }

    $written = 0L
    $sw = [System.Diagnostics.Stopwatch]::StartNew()

    Write-Host "Schreiben läuft ..." -ForegroundColor Magenta
    Safe-WriteLog -LogPath $LogPath -Message ("Schreiben gestartet | Datei: {0} | Ziel: {1} | Block: {2}" -f `
        $FilePath, (Format-Bytes $TargetBytes), (Format-Bytes $BlockSizeBytes))

    $fs = $null

    try {
        $fs = [System.IO.File]::Open(
            $FilePath,
            [System.IO.FileMode]::Create,
            [System.IO.FileAccess]::Write,
            [System.IO.FileShare]::None
        )

        while ($written -lt $TargetBytes) {
            $remaining = $TargetBytes - $written
            $toWrite = if ($remaining -ge $BlockSizeBytes) { [int]$BlockSizeBytes } else { [int]$remaining }

            $fs.Write($buffer, 0, $toWrite)
            $written += $toWrite

            $percent = [math]::Round(($written / $TargetBytes) * 100, 2)
            $speedMBs = if ($sw.Elapsed.TotalSeconds -gt 0) {
                [math]::Round(($written / 1MB) / $sw.Elapsed.TotalSeconds, 2)
            } else {
                0
            }

            Write-Progress -Activity "Schreiben läuft" `
                -Status ("{0} / {1} | {2} MB/s" -f (Format-Bytes $written), (Format-Bytes $TargetBytes), $speedMBs) `
                -PercentComplete $percent
        }

        $fs.Flush($true)
        Write-Progress -Activity "Schreiben läuft" -Completed
    }
    finally {
        if ($fs) { $fs.Dispose() }
    }

    $sw.Stop()
    $seconds = [math]::Round($sw.Elapsed.TotalSeconds, 2)
    $speed = if ($seconds -gt 0) { [math]::Round(($written / 1MB) / $seconds, 2) } else { 0 }

    Safe-WriteLog -LogPath $LogPath -Message ("Schreiben abgeschlossen | Geschrieben: {0} | Dauer: {1}s | MB/s: {2}" -f `
        (Format-Bytes $written), $seconds, $speed)

    return [PSCustomObject]@{
        WrittenBytes = $written
        Seconds      = $seconds
        SpeedMBs     = $speed
        Mode         = "Fixed"
    }
}

function Write-TestFileMaxFill {
    param(
        [string]$FilePath,
        [Int64]$BlockSizeBytes,
        [string]$LogPath
    )

    $buffer = New-Object byte[] $BlockSizeBytes
    for ($i = 0; $i -lt $buffer.Length; $i++) {
        $buffer[$i] = 0xAA
    }

    $written = 0L
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $fullReached = $false
    $lastFreeBytes = 0L

    Write-Host "Schreiben läuft ..." -ForegroundColor Magenta
    Safe-WriteLog -LogPath $LogPath -Message ("Schreiben gestartet | Datei: {0} | Ziel: maximal frei | Block: {1}" -f `
        $FilePath, (Format-Bytes $BlockSizeBytes))

    $fs = $null

    try {
        $fs = [System.IO.File]::Open(
            $FilePath,
            [System.IO.FileMode]::Create,
            [System.IO.FileAccess]::Write,
            [System.IO.FileShare]::None
        )

        while ($true) {
            try {
                $fs.Write($buffer, 0, [int]$BlockSizeBytes)
                $written += $BlockSizeBytes

                $driveLetter = [System.IO.Path]::GetPathRoot($FilePath).TrimEnd('\')
                $lastFreeBytes = Get-CurrentFreeBytes -DriveLetter $driveLetter
                $speedMBs = if ($sw.Elapsed.TotalSeconds -gt 0) {
                    [math]::Round(($written / 1MB) / $sw.Elapsed.TotalSeconds, 2)
                } else {
                    0
                }

                Write-Progress -Activity "Schreiben läuft" `
                    -Status ("Geschrieben: {0} | Verbleibend: {1} | {2} MB/s" -f `
                        (Format-Bytes $written), (Format-Bytes $lastFreeBytes), $speedMBs) `
                    -PercentComplete -1
            }
            catch [System.IO.IOException] {
                $fullReached = $true
                break
            }
        }

        try {
            $fs.Flush($true)
        }
        catch {
        }

        Write-Progress -Activity "Schreiben läuft" -Completed
    }
    finally {
        if ($fs) { $fs.Dispose() }
    }

    $sw.Stop()
    $seconds = [math]::Round($sw.Elapsed.TotalSeconds, 2)
    $speed = if ($seconds -gt 0) { [math]::Round(($written / 1MB) / $seconds, 2) } else { 0 }

    if (-not $fullReached) {
        throw "Schreibvorgang wurde beendet, ohne dass ein voller Datenträger erkannt wurde."
    }

    Safe-WriteLog -LogPath $LogPath -Message ("Schreiben abgeschlossen | Maximalmodus | Geschrieben: {0} | Dauer: {1}s | MB/s: {2}" -f `
        (Format-Bytes $written), $seconds, $speed)

    return [PSCustomObject]@{
        WrittenBytes = $written
        Seconds      = $seconds
        SpeedMBs     = $speed
        Mode         = "Max"
    }
}

function Remove-TestFile {
    param(
        [string]$FilePath,
        [string]$LogPath
    )

    if (Test-Path $FilePath) {
        Write-Host "Löschen läuft ..." -ForegroundColor Magenta
        Safe-WriteLog -LogPath $LogPath -Message ("Löschen gestartet | Datei: {0}" -f $FilePath)

        $sw = [System.Diagnostics.Stopwatch]::StartNew()
        Remove-Item $FilePath -Force -ErrorAction Stop
        $sw.Stop()

        $seconds = [math]::Round($sw.Elapsed.TotalSeconds, 2)
        Safe-WriteLog -LogPath $LogPath -Message ("Löschen abgeschlossen | Dauer: {0}s" -f $seconds)
    }
}

$targetFilePath = $null
$logPath = $null

try {
    Clear-Host
    Write-Section "Festplatten-Stresstest per Testdatei"

    $selectedDrive = Select-Drive
    Show-DriveWarnings -Drive $selectedDrive

    $targetFolder = Get-TargetFolder -Drive $selectedDrive
    Test-WritePermission -FolderPath $targetFolder
    Run-MiniWriteTest -FolderPath $targetFolder

    $testFileName = Get-TestFileName
    $logFileName = Get-LogFileName -TestFileName $testFileName

    $targetFilePath = Join-Path $targetFolder $testFileName
    $logPath = Join-Path $targetFolder $logFileName

    Initialize-Log -LogPath $logPath
    Handle-ExistingFile -FilePath $targetFilePath

    $sizeMode = Get-SizeMode
    $fixedSizeBytes = 0L
    if ($sizeMode -eq "2") {
        $fixedSizeBytes = Get-FixedSizeBytes
    }

    $loopCount = Get-LoopCount
    $blockSizeBytes = Get-BlockSizeBytes

    Confirm-SystemDriveIfNeeded -DriveLetter $selectedDrive.DeviceID

    $currentFreeBytes = Get-CurrentFreeBytes -DriveLetter $selectedDrive.DeviceID
    $previewText = if ($sizeMode -eq "1") { "Maximal bis Laufwerk voll meldet" } else { (Format-Bytes $fixedSizeBytes) }

    Write-Section "Zusammenfassung"
    Write-Host ("Laufwerk:        {0}" -f $selectedDrive.DeviceID) -ForegroundColor Green
    Write-Host ("Zielordner:      {0}" -f $targetFolder) -ForegroundColor Green
    Write-Host ("Testdatei:       {0}" -f $targetFilePath) -ForegroundColor Green
    Write-Host ("Logdatei:        {0}" -f $logPath) -ForegroundColor Green
    Write-Host ("Freier Platz:    {0}" -f (Format-Bytes $currentFreeBytes)) -ForegroundColor Green
    Write-Host ("Modus:           {0}" -f $(if ($sizeMode -eq "1") { "Maximal" } else { "Feste Grösse" })) -ForegroundColor Green
    Write-Host ("Schreibmenge:    {0}" -f $previewText) -ForegroundColor Green
    Write-Host ("Blockgrösse:     {0}" -f (Format-Bytes $blockSizeBytes)) -ForegroundColor Green
    Write-Host ("Durchläufe:      {0}" -f $(if ($loopCount -eq -1) { "Unendlich bis CTRL+C" } else { $loopCount })) -ForegroundColor Green

    Safe-WriteLog -LogPath $logPath -Message "========================================"
    Safe-WriteLog -LogPath $logPath -Message "Neuer Lauf gestartet"
    Safe-WriteLog -LogPath $logPath -Message ("Laufwerk: {0}" -f $selectedDrive.DeviceID)
    Safe-WriteLog -LogPath $logPath -Message ("Zielordner: {0}" -f $targetFolder)
    Safe-WriteLog -LogPath $logPath -Message ("Testdatei: {0}" -f $targetFilePath)
    Safe-WriteLog -LogPath $logPath -Message ("Freier Platz zu Beginn: {0}" -f (Format-Bytes $currentFreeBytes))
    Safe-WriteLog -LogPath $logPath -Message ("Modus: {0}" -f $(if ($sizeMode -eq "1") { "Maximal" } else { "Feste Grösse" }))
    Safe-WriteLog -LogPath $logPath -Message ("Blockgrösse: {0}" -f (Format-Bytes $blockSizeBytes))
    Safe-WriteLog -LogPath $logPath -Message ("Durchläufe: {0}" -f $(if ($loopCount -eq -1) { "Unendlich" } else { $loopCount }))

    $confirmStart = Read-MenuChoice -Prompt "Test starten? (j/n)" -AllowedValues @("j","J","n","N")
    if ($confirmStart -notin @("j","J")) {
        throw "Abgebrochen."
    }

    Start-Countdown -Seconds $script:DefaultCountdownSeconds

    $iteration = 0
    while ($true) {
        $iteration++

        Write-Section ("Durchlauf {0}" -f $iteration)
        Safe-WriteLog -LogPath $logPath -Message ("Durchlauf {0} gestartet" -f $iteration)

        $freeBytesNow = Get-CurrentFreeBytes -DriveLetter $selectedDrive.DeviceID
        Write-Host ("Aktuell frei:     {0}" -f (Format-Bytes $freeBytesNow)) -ForegroundColor DarkGray
        Safe-WriteLog -LogPath $logPath -Message ("Durchlauf {0} | Freier Platz jetzt: {1}" -f $iteration, (Format-Bytes $freeBytesNow))

        if ($sizeMode -eq "1") {
            $result = Write-TestFileMaxFill -FilePath $targetFilePath -BlockSizeBytes $blockSizeBytes -LogPath $logPath
        }
        else {
            Write-Host ("Schreibmenge:     {0}" -f (Format-Bytes $fixedSizeBytes)) -ForegroundColor DarkGray
            Safe-WriteLog -LogPath $logPath -Message ("Durchlauf {0} | Schreibmenge: {1}" -f $iteration, (Format-Bytes $fixedSizeBytes))

            $result = Write-TestFileFixedSize `
                -FilePath $targetFilePath `
                -TargetBytes $fixedSizeBytes `
                -BlockSizeBytes $blockSizeBytes `
                -LogPath $logPath
        }

        Remove-TestFile -FilePath $targetFilePath -LogPath $logPath

        Write-Host ("Durchlauf {0} abgeschlossen | Geschrieben: {1} | Dauer: {2} s | ca. {3} MB/s" -f `
            $iteration, (Format-Bytes $result.WrittenBytes), $result.Seconds, $result.SpeedMBs) -ForegroundColor Green

        Safe-WriteLog -LogPath $logPath -Message ("Durchlauf {0} abgeschlossen | Geschrieben: {1} | Dauer: {2}s | MB/s: {3}" -f `
            $iteration, (Format-Bytes $result.WrittenBytes), $result.Seconds, $result.SpeedMBs)

        if ($loopCount -ne -1 -and $iteration -ge $loopCount) {
            break
        }
    }

    Write-Section "Fertig"
    Write-Host "Test abgeschlossen." -ForegroundColor Green
    Safe-WriteLog -LogPath $logPath -Message "Test abgeschlossen"
}
catch {
    $msg = $_.Exception.Message
    Write-ErrorLine ""
    Write-ErrorLine ("Fehler: {0}" -f $msg)
    Safe-WriteLog -LogPath $logPath -Message ("Fehler: {0}" -f $msg)
}
finally {
    try {
        if ($targetFilePath -and (Test-Path $targetFilePath)) {
            Write-WarnLine "Es wurde versucht, eine verbliebene Testdatei aufzuräumen ..."
            Remove-Item $targetFilePath -Force -ErrorAction SilentlyContinue
            Safe-WriteLog -LogPath $logPath -Message "Cleanup ausgeführt, verbliebene Testdatei entfernt sofern vorhanden"
        }
    }
    catch {
    }

    Write-Host ""
    Write-WarnLine "Hinweis: Bei Abbruch per CTRL+C wurde versucht, eine verbliebene Testdatei zu löschen."
    if ($logPath) {
        Write-Host ("Log: {0}" -f $logPath) -ForegroundColor DarkGray
    }
}

Start in der Powershell mit:
Set-ExecutionPolicy -Scope Process Bypass
.\fill-drive-loop.ps1
 
  • Gefällt mir
Reaktionen: Asghan, iSight2TheBlind, cyberpirate und eine weitere Person
Ich denke, die User hier interessiert einfach, was Du damit erreichen willst.
Die Dauerhaltbarkeit kannst Du zumindest bei HDDs nicht sinnvoll testen.
Einzig bei SSDs kann man ggf. feststellen wieviele Daten wirklich darauf geschrieben werden können und ob die Angaben der Hersteller in etwa stimmen.

Cunhell

PS: Aber die SSD ist halt dann kaputt geschrieben
 
Zuletzt bearbeitet: (PS Hinzugefügt)
  • Gefällt mir
Reaktionen: Darkman.X
Ist ja nicht so, als ob das noch niemand gemacht hätte. Und festgestellt hat, das eine SSD Petabyte verträgt, bevor sie den Geist aufgibt. Wie lange hast du deinen Test geplant? Wird mehrere Jahre dauern.
 
  • Gefällt mir
Reaktionen: cyberpirate
cunhell schrieb:
Einzig bei SSDs kann man ggf. feststellen wieviele Daten wirklich darauf geschrieben werden können
Nicht einmal da, weil jedes Exemplar unterschiedliche Güten hat. Sofern also nicht der Plan ist aus diversen Chargen statistisch relevante Stückzahlen, also allerwenigstens tausende Laufwerke zu testen, frage ch mich wirklich nach dem Ziel ausser Altgerät und mal mas Lustiges machen.
 
  • Gefällt mir
Reaktionen: iSight2TheBlind
Dauerstresstest ist bei Laufwerken, Festplatten und SSDs halt maximal sinnlos.
Auf die Haltbarkeit kann man dadurch keine Rückschlüsse ziehen und durch so einen "Test" nutzt du das Laufwerk nur massiv ab.
Das heißt, egal was bei dem Test herauskommt, die Lebensdauer wird sich alleine durch den Test bereits signifikant verringern.

Es wäre halt interessant zu erfahren aus welchem Grund du so was machen willst.

Es geht hier offensichtlich nicht um Datenschutz/Überschreiben. Auch da reicht einmaliges Überschreiben vollkommen aus.
Und wenn man eine Festplatte hat mit Secure Erase, Instant Secure Erase usw. muss man gar nichts überschreiben, hier reicht ein Löschen der Schlüssel vollkommen aus.

Bei Flashlaufwerken kann man je nach Modell und Kaufpreis auf einen Fake reinfallen. Da wäre es sinnvoll den Datenträger zumindest einmal vollzuschreiben, um zu schauen wie viel tatsächlich drauf passt.

Aber bei klassischen Festplatten steht Abnutzung des Laufwerks, Dauer und Resultat/Aussagekraft so eines Tests im keinem sinnvollen Verhältnis.

Bei klassischen Festplatten ist es oft so das diese gerne spontan Ausfälle zeigen. Bis zu diesem Moment ist nicht erkennbar das ein Ausfall möglicherweise kurz bevor steht. Da hast du dann plötzlich bad sectors, die am Tag zuvor noch einwandfrei funktionierten. Und ist das einmal passiert, wird das Problem nur größer, bis zum endgültigen Ausfall.

Ich habe z.B. noch zwei klassische Festplatten im Rechner, beide mit inzwischen knapp 25.000 Betriebsstunden.
Die sind beide einwandfrei in Ordnung. Keine nicht korrigierbaren Sektoren, keine ausstehenden Sektoren, keine wiederzugewiesenen Sektoren usw.
Kein Test der Welt kann mir und dir sagen wie lange es noch dauert bis zum ersten Problem oder ob es bis ich die mal rauswerfe überhaupt zu einem Problem kommt.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Darkman.X, cunhell und wern001
Zurück
Oben