Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: metal-toolbox/ironlib
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 9c0a8e4014be2475c1ccb8dbfbf137ef734ad307
Choose a base ref
..
head repository: metal-toolbox/ironlib
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c96a04929badba728503e44020bc53947e343d18
Choose a head ref
Showing with 201 additions and 4 deletions.
  1. +21 −2 actions/storage_controller.go
  2. +1 −2 examples/diskwipe/main.go
  3. +109 −0 utils/watermark_disk.go
  4. +70 −0 utils/watermark_disk_test.go
23 changes: 21 additions & 2 deletions actions/storage_controller.go
Original file line number Diff line number Diff line change
@@ -100,6 +100,25 @@ func (s *StorageControllerAction) WipeDisk(ctx context.Context, logicalName stri
if err != nil {
return err
}

return util.WipeDisk(ctx, logicalName)
// Watermark disk
// Before wiping the disk, we apply watermarks to later verify successful deletion
log.Printf("%s | Initiating watermarking process", logicalName)
check, err := utils.ApplyWatermarks(logicalName)
if err != nil {
return err
}
// Wipe the disk
err = util.WipeDisk(ctx, logicalName)
if err != nil {
return err
}
// Check if the watermark has been removed after wiping
log.Printf("%s | Checking if the watermark has been removed", logicalName)
err = check()
if err != nil {
return err
}
// Watermarks have been successfully removed, indicating successful deletion
log.Printf("%s | Watermarks has been removed", logicalName)
return nil
}
3 changes: 1 addition & 2 deletions examples/diskwipe/main.go
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@ package main

import (
"context"
"fmt"
"time"

"github.com/metal-toolbox/ironlib/actions"
@@ -22,5 +21,5 @@ func main() {
if err != nil {
logger.Fatal(err)
}
fmt.Println("Wiped")
logger.Println("Wiped successfully!")
}
109 changes: 109 additions & 0 deletions utils/watermark_disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package utils

import (
"crypto/rand"
"fmt"
"io"
"math/big"
"os"
"slices"

"github.com/pkg/errors"
)

const (
bufferSize = 512
numWatermarks = 10
)

type watermark struct {
position int64
data []byte
}

// ApplyWatermarks applies watermarks to the specified disk.
// It returns a function that checks if the applied watermarks still exist on the file.
// It relies on the writeWatermarks function to uniformly write watermarks across the disk.
func ApplyWatermarks(logicalName string) (func() error, error) {
// Write open
file, err := os.OpenFile(logicalName, os.O_WRONLY, 0)
if err != nil {
return nil, err
}
defer file.Close()

// Get disk or partition size
fileSize, err := file.Seek(0, io.SeekEnd)
if err != nil {
return nil, err
}

if fileSize == 0 {
return nil, errors.New("No space for watermarking")
}

// Write watermarks on random locations
watermarks, err := writeWatermarks(file, fileSize, numWatermarks)
if err != nil {
return nil, err
}

checker := func() error {
file, err := os.OpenFile(logicalName, os.O_RDONLY, 0)
if err != nil {
return err
}
defer file.Close()

for _, watermark := range watermarks {
_, err = file.Seek(watermark.position, io.SeekStart)
if err != nil {
return err
}
// Read the watermark written to the position
currentValue := make([]byte, bufferSize)
_, err = io.ReadFull(file, currentValue)
if err != nil {
return err
}
// Check if the watermark is still in the disk
if slices.Equal(currentValue, watermark.data) {
ErrorExistingWatermark := errors.New("Error existing watermark in the position: ")
return fmt.Errorf("%s@%d | %w", logicalName, watermark.position, ErrorExistingWatermark)
}
}
return nil
}
return checker, nil
}

// writeWatermarks creates random watermarks and writes them uniformly into a given file.
func writeWatermarks(file *os.File, fileSize, count int64) ([]watermark, error) {
origin := int64(0)
intervalSize := fileSize / count
watermarks := make([]watermark, count)
for i := 0; i < numWatermarks; i++ {
data := make([]byte, bufferSize)
_, err := rand.Read(data)
if err != nil {
return nil, err
}
offset, err := rand.Int(rand.Reader, big.NewInt(intervalSize))
if err != nil {
return nil, err
}
randomPosition := int64(offset.Uint64()) + origin - bufferSize
_, err = file.Seek(randomPosition, io.SeekStart)
if err != nil {
return nil, err
}
_, err = file.Write(data)
if err != nil {
return nil, err
}
watermarks[i].position = randomPosition
watermarks[i].data = data
origin += intervalSize
}
return watermarks, nil
}
70 changes: 70 additions & 0 deletions utils/watermark_disk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package utils

import (
"crypto/rand"
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func Test_ApplyWatermarks(t *testing.T) {
// Create a temporary file
tempFile, err := os.CreateTemp("", "testfile")
if err != nil {
t.Fatalf("Failed to create temporary file: %v", err)
}
defer os.Remove(tempFile.Name())

// Close the file since we'll be reopening it in ApplyWatermarks
tempFile.Close()

t.Run("NegativeTest", func(t *testing.T) {
// Create a ~15KB empty file, no room for all watermarks
err := os.WriteFile(tempFile.Name(), make([]byte, 1*1024), 0o600)
if err != nil {
t.Fatalf("Failed to create empty file: %v", err)
}
// Create a 1KB empty file, no room for all watermarks
assert.NoError(t, os.Truncate(tempFile.Name(), 1*1024))
// Apply watermarks and expect an error
checker, _ := ApplyWatermarks(tempFile.Name())
assert.Nil(t, checker)
})

t.Run("EmptyFile", func(t *testing.T) {
// Wipe the file
assert.NoError(t, os.Truncate(tempFile.Name(), 0))

// Apply watermarks and expect no error
checker, err := ApplyWatermarks(tempFile.Name())
assert.Error(t, err, "No space for watermarking")
assert.Nil(t, checker)
})
t.Run("PositiveTestWithRandomDataAndWipe", func(t *testing.T) {
// Write the file full of random data
randomData := make([]byte, 15*1024*1024)
_, err := rand.Read(randomData)
if err != nil {
t.Fatalf("Failed to generate random data: %v", err)
}
err = os.WriteFile(tempFile.Name(), randomData, 0o600)
if err != nil {
t.Fatalf("Failed to write random data to file: %v", err)
}

// Apply watermarks and expect no error
checker, err := ApplyWatermarks(tempFile.Name())
if err != nil {
t.Fatalf("Error applying watermarks: %v", err)
}
// simulate wipe
assert.NoError(t, os.Truncate(tempFile.Name(), 0))
assert.NoError(t, os.Truncate(tempFile.Name(), 15*1024*1024))

err = checker()
if err != nil {
t.Errorf("Expected no error, got: %v", err)
}
})
}