-
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 detect ineffective wipes (#135)
* ironlib is able to detect ineffective wipes using watermarks * Watermarks scattered evenly * return err propagation to ApplyWatermarks and more documentation to the divide and conquer writeWatermarks function * rewrite writeWatermarks function from recursive to iterative * Added tests to ApplyWatermarks func and more verbose err * Added more comments for code understanding and function usage. Also reduced test code using assert functions
- Loading branch information
1 parent
af14e38
commit 037a39a
Showing
4 changed files
with
201 additions
and
4 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,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 | ||
} |
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,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) | ||
} | ||
}) | ||
} |