107 lines
2.1 KiB
Go
107 lines
2.1 KiB
Go
package shred
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// A FileInfo describes a file and is returned by Stat.
|
|
type FileInfo interface {
|
|
Name() string // base name of the file
|
|
Size() int64 // length in bytes for regular files; system-dependent for others
|
|
Mode() fs.FileMode // file mode bits
|
|
ModTime() time.Time // modification time
|
|
IsDir() bool // abbreviation for Mode().IsDir()
|
|
Sys() any // underlying data source (can return nil)
|
|
}
|
|
|
|
type File interface {
|
|
Seek(int64, int) (int64, error)
|
|
Sync() error
|
|
Write([]byte) (int, error)
|
|
WriteAt([]byte, int64) (int, error)
|
|
Close() error
|
|
}
|
|
|
|
type ProgressBar interface {
|
|
Add(int) error
|
|
Write([]byte) (int, error)
|
|
Finish() error
|
|
}
|
|
|
|
type ShredderOptions struct {
|
|
Zero bool
|
|
Iterations int
|
|
Bar ProgressBar
|
|
}
|
|
|
|
type Shredder struct {
|
|
options *ShredderOptions
|
|
}
|
|
|
|
func New(opts *ShredderOptions) *Shredder {
|
|
return &Shredder{opts}
|
|
}
|
|
|
|
func (s *Shredder) Write(pathName string) error {
|
|
// Stat the file for the file length
|
|
fstat, err := os.Stat(pathName)
|
|
if err != nil {
|
|
return fmt.Errorf("file stat failed: %w", err)
|
|
}
|
|
|
|
// Open the file
|
|
file, err := os.OpenFile(pathName, os.O_WRONLY, fstat.Mode())
|
|
if err != nil {
|
|
return fmt.Errorf("file opening failed: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
err = s.shred(fstat, file)
|
|
if err != nil {
|
|
return fmt.Errorf("shredding failed: %w", err)
|
|
}
|
|
|
|
if s.options.Zero {
|
|
if err := os.Truncate(pathName, 0); err != nil {
|
|
return fmt.Errorf("truncate failed: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Shredder) shred(fstat FileInfo, file File) error {
|
|
fSize := fstat.Size()
|
|
|
|
// Avoid shredding if the file is already empty
|
|
if fSize == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Write random bytes over the file N times
|
|
junkBuf := make([]byte, fstat.Size())
|
|
for i := 0; i < s.options.Iterations; i++ {
|
|
_, err := rand.Read(junkBuf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = file.WriteAt(junkBuf, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = file.Sync()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Add written bytes to progress bar
|
|
_, _ = s.options.Bar.Write(junkBuf)
|
|
}
|
|
|
|
return nil
|
|
}
|