-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ironlib is able to fill a disk with all zeros (#131)
* Add skeleton of DiskWiper Signed-off-by: Ignacio Turégano <[email protected]> * added zero wipe logic Signed-off-by: Ignacio Turégano <[email protected]> * example of disk wipe with zeroes Signed-off-by: Ignacio Turégano <[email protected]> * zero wipe unit tests Signed-off-by: Ignacio Turégano <[email protected]> * fix example description Signed-off-by: Ignacio Turégano <[email protected]> * translate comments to english Signed-off-by: Ignacio Turégano <[email protected]> * Added speed, eta and device to the output Signed-off-by: Ignacio Turégano <[email protected]> * Fix defects and include improvements from PR #131 * Refactor zero wipe to fill with zeros from PR #131 * Modified the code to track the remaining bytes and handle the nearly final scenario when the buffer size exceeds the remaining disk space to delete. Also, returning 'magic' numbers * Added function to print progress and reverted calculation to avoid a integer divide by zero * Exceptions for shadowing err and magic numbers in utils * Testing against a range of file sizes (also a non-existent file) * Removed magic numbers exception and checked file.Sync() * golangci-lint to tests * using log to be goroutine safe * Removed the verbose err and reduced the printProgress to just the print data * Check if the context has been canceled during disk wipe * new linter rules, modified code to satisfy them * Conditioning log to trace variable * gofumpt fill_zero_test --------- Signed-off-by: Ignacio Turégano <[email protected]>
- Loading branch information
1 parent
8f46009
commit af14e38
Showing
5 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/metal-toolbox/ironlib/actions" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
// This example invokes ironlib and wipes the disk /dev/sdZZZ with a timeout of 1 day | ||
|
||
func main() { | ||
logger := logrus.New() | ||
logger.Formatter = new(logrus.JSONFormatter) | ||
logger.SetLevel(logrus.TraceLevel) | ||
sca := actions.NewStorageControllerAction(logger) | ||
ctx, cancel := context.WithTimeout(context.Background(), 86400*time.Second) | ||
defer cancel() | ||
err := sca.WipeDisk(ctx, "/dev/sdZZZ") | ||
if err != nil { | ||
logger.Fatal(err) | ||
} | ||
fmt.Println("Wiped") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package utils | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"log" | ||
"os" | ||
"time" | ||
) | ||
|
||
type FillZero struct { | ||
Quiet bool | ||
} | ||
|
||
// Return a new zerowipe executor | ||
func NewFillZeroCmd(trace bool) *FillZero { | ||
z := FillZero{} | ||
if !trace { | ||
z.SetQuiet() | ||
} | ||
return &z | ||
} | ||
|
||
func (z *FillZero) WipeDisk(ctx context.Context, logicalName string) error { | ||
log.Println("Starting zero-fill of", logicalName) | ||
// Write open | ||
file, err := os.OpenFile(logicalName, os.O_WRONLY, 0) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
// Get disk or partition size | ||
partitionSize, err := file.Seek(0, io.SeekEnd) | ||
if err != nil { | ||
return err | ||
} | ||
log.Printf("%s | Size: %dB\n", logicalName, partitionSize) | ||
_, err = file.Seek(0, io.SeekStart) | ||
if err != nil { | ||
return err | ||
} | ||
var bytesSinceLastPrint int64 | ||
var totalBytesWritten int64 | ||
buffer := make([]byte, 4096) | ||
start := time.Now() | ||
for bytesRemaining := partitionSize; bytesRemaining > 0; { | ||
// Check if the context has been canceled | ||
select { | ||
case <-ctx.Done(): | ||
log.Println("Context canceled. Stopping WipeDisk") | ||
return ctx.Err() | ||
default: | ||
l := min(int64(len(buffer)), bytesRemaining) | ||
bytesWritten, writeError := file.Write(buffer[:l]) | ||
if writeError != nil { | ||
return writeError | ||
} | ||
totalBytesWritten += int64(bytesWritten) | ||
bytesSinceLastPrint += int64(bytesWritten) | ||
bytesRemaining -= int64(bytesWritten) | ||
// Print progress report every 10 seconds and when done | ||
if bytesRemaining == 0 || time.Since(start) >= 10*time.Second { | ||
printProgress(totalBytesWritten, partitionSize, &start, &bytesSinceLastPrint, logicalName) | ||
start = time.Now() | ||
bytesSinceLastPrint = 0 | ||
} | ||
} | ||
} | ||
err = file.Sync() | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func printProgress(totalBytesWritten, partitionSize int64, start *time.Time, bytesSinceLastPrint *int64, path string) { | ||
// Calculate progress and ETA | ||
progress := float64(totalBytesWritten) / float64(partitionSize) * 100 | ||
elapsed := time.Since(*start).Seconds() | ||
speed := float64(*bytesSinceLastPrint) / elapsed // Speed in bytes per second | ||
remainingSeconds := (float64(partitionSize) - float64(totalBytesWritten)) / speed // Remaining time in seconds | ||
remainingHours := float64(remainingSeconds / 3600) | ||
mbPerSecond := speed / (1024 * 1024) | ||
log.Printf("%s | Progress: %.2f%% | Speed: %.2f MB/s | Estimated time left: %.2f hour(s)\n", path, progress, mbPerSecond, remainingHours) | ||
} | ||
|
||
// We are in go 1.19 min not available yet | ||
func min(a, b int64) int64 { | ||
if a < b { | ||
return a | ||
} | ||
return b | ||
} | ||
|
||
// SetQuiet lowers the verbosity | ||
func (z *FillZero) SetQuiet() { | ||
z.Quiet = true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package utils | ||
|
||
import ( | ||
"context" | ||
"os" | ||
"strconv" | ||
"testing" | ||
) | ||
|
||
func Test_NewFillZeroCmd(t *testing.T) { | ||
// Test if NewFillZeroCmd returns a non-nil pointer | ||
zw := NewFillZeroCmd(false) | ||
if zw == nil { | ||
t.Error("Expected non-nil pointer, got nil") | ||
} | ||
} | ||
|
||
func Test_WipeDisk(t *testing.T) { | ||
for _, size := range []int{4095, 4096, 4097, 8192} { | ||
t.Run(strconv.Itoa(size), func(t *testing.T) { | ||
// Create a temporary file for testing | ||
tmpfile, err := os.CreateTemp("", "example") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer os.Remove(tmpfile.Name()) // clean up | ||
// Write some content to the temporary file | ||
expectedSize := int64(4096) | ||
if _, err = tmpfile.Write(make([]byte, expectedSize)); err != nil { | ||
t.Fatal(err) | ||
} | ||
// Simulate a context | ||
ctx := context.Background() | ||
// Create a FillZero instance | ||
zw := &FillZero{} | ||
// Test Fill function | ||
err = zw.WipeDisk(ctx, tmpfile.Name()) | ||
if err != nil { | ||
t.Errorf("Fill returned an error: %v", err) | ||
} | ||
// Check if the file size remains the same after overwrite | ||
fileInfo, err := os.Stat(tmpfile.Name()) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if size := fileInfo.Size(); size != expectedSize { | ||
t.Errorf("Expected file size to remain %d after overwrite, got %d", expectedSize, size) | ||
} | ||
}) | ||
} | ||
} |