From 8b51eb3f5c2a410205463d99655fc8b76bf1b1c5 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 9 Sep 2020 15:52:34 -0400 Subject: [PATCH] Make Wipe() wipe partitions, Add demo disk wipe. Previously, system.Wipe() would only wipe the first and last MiB of the disk. Now, it will also wipe the first and last MiB of any partitions on the disk. Also, add 'demo disk wipe' subcommand that calls this. --- demo/disk.go | 49 +++++++++++------- linux/disk.go | 23 +++++++-- linux/disk_test.go | 120 ++++++++++++++++++++++++++++++++++++++++----- linux/system.go | 2 +- 4 files changed, 161 insertions(+), 33 deletions(-) diff --git a/demo/disk.go b/demo/disk.go index 6377c7b..7413da4 100644 --- a/demo/disk.go +++ b/demo/disk.go @@ -30,6 +30,12 @@ var diskCommands = cli.Command{ Usage: "Scan disks on the system and dump data (human)", Action: diskShow, }, + { + Name: "wipe", + Usage: ("Quickly wipe disks on the system. Zero any existing " + + "beginning and end of disk and any existing partitions"), + Action: diskWipe, + }, }, } @@ -43,6 +49,7 @@ func diskScan(c *cli.Context) error { } if c.Args().Len() == 1 { + // a single argument will only output 1 disk, not an array of one disk. disk, err := mysys.ScanDisk(c.Args().First()) if err != nil { return err @@ -78,37 +85,45 @@ func diskScan(c *cli.Context) error { } func diskShow(c *cli.Context) error { - var err error - mysys := linux.System() - matchAll := func(d disko.Disk) bool { - return true + disks, err := getDiskSet(mysys, c.Args().Slice()...) + + if err != nil { + return err } - if c.Args().Len() == 1 { - disk, err := mysys.ScanDisk(c.Args().First()) - if err != nil { - return err - } + for _, d := range disks { + fmt.Printf("%s\n%s\n", d.String(), d.Details()) + } - fmt.Printf("%s\n%s\n", disk.String(), disk.Details()) + return nil +} - return nil +func getDiskSet(mysys disko.System, paths ...string) (disko.DiskSet, error) { + matchAll := func(d disko.Disk) bool { + return true } - var disks disko.DiskSet - if c.Args().Len() == 0 { - disks, err = mysys.ScanAllDisks(matchAll) - } else { - disks, err = mysys.ScanDisks(matchAll, c.Args().Slice()...) + if len(paths) == 0 || (len(paths) == 1 && paths[0] == "all") { + return mysys.ScanAllDisks(matchAll) } + return mysys.ScanDisks(matchAll, paths...) +} + +func diskWipe(c *cli.Context) error { + mysys := linux.System() + + disks, err := getDiskSet(mysys, c.Args().Slice()...) + if err != nil { return err } for _, d := range disks { - fmt.Printf("%s\n%s\n", d.String(), d.Details()) + if err = mysys.Wipe(d); err != nil { + return err + } } return nil diff --git a/linux/disk.go b/linux/disk.go index dbe52bc..bb372ef 100644 --- a/linux/disk.go +++ b/linux/disk.go @@ -326,14 +326,31 @@ func getPartName(s string) [72]byte { return b } -func zeroPathStartEnd(fpath string, start int64, last int64) error { - fp, err := os.OpenFile(fpath, os.O_RDWR, 0) +func wipeDisk(disk disko.Disk) error { + fp, err := os.OpenFile(disk.Path, os.O_RDWR, 0) if err != nil { return err } defer fp.Close() - return zeroStartEnd(fp, start, last) + if err := zeroStartEnd(fp, int64(0), int64(disk.Size)); err != nil { + return err + } + + for _, p := range disk.Partitions { + // The point of this operation is to wipe. Avoid out of range errors + // that could happen as part of a bad partition table. + end := disk.Size + if end > p.Last { + end = disk.Size + } + + if err := zeroStartEnd(fp, int64(p.Start), int64(end)); err != nil { + return err + } + } + + return nil } // zeroStartEnd - zero the start and end provided with 1MiB bytes of zeros. diff --git a/linux/disk_test.go b/linux/disk_test.go index c772c17..3d436b2 100644 --- a/linux/disk_test.go +++ b/linux/disk_test.go @@ -2,6 +2,7 @@ package linux import ( "fmt" + "io" "io/ioutil" "os" "path" @@ -117,9 +118,8 @@ func TestGetAttachType(t *testing.T) { })) } -func genTempGptDisk(tmpd string) (disko.Disk, error) { +func genTempGptDisk(tmpd string, fsize uint64) (disko.Disk, error) { fpath := path.Join(tmpd, "mydisk") - fsize := uint64(200 * 1024 * 1024) // nolint:gomnd disk := disko.Disk{ Name: "mydisk", @@ -141,19 +141,22 @@ func genTempGptDisk(tmpd string) (disko.Disk, error) { return disk, fmt.Errorf("Expected 1 free space, found %d", fs) } - part := disko.Partition{ - Start: fs[0].Start, - Last: fs[0].Last, - Type: partid.LinuxLVM, - Name: "mytest partition", - ID: disko.GenGUID(), - Number: uint(1), - } + parts := disko.PartitionSet{ + 1: disko.Partition{ + Start: fs[0].Start, + Last: fs[0].Last, + Type: partid.LinuxLVM, + Name: "mytest partition", + ID: disko.GenGUID(), + Number: uint(1), + }} - if err := addPartitionSet(disk, disko.PartitionSet{part.Number: part}); err != nil { + if err := addPartitionSet(disk, parts); err != nil { return disk, err } + disk.Partitions = parts + return disk, nil } @@ -298,6 +301,99 @@ func TestMyPartitionMBR(t *testing.T) { } } +// nolint: funlen +func TestWipeDisk(t *testing.T) { + tmpd, err := ioutil.TempDir("", "disko_test") + if err != nil { + t.Fatalf("Failed to create tempdir: %s", err) + } + + mib := uint64(1024 * 1024) // nolint: gomnd + + defer os.RemoveAll(tmpd) + + disk, err := genTempGptDisk(tmpd, 50*mib) // nolint:gomnd + if err != nil { + t.Fatalf("Creation of temp disk failed: %s", err) + } + + if len(disk.Partitions) == 0 { + t.Fatalf("Found no partitions on the disk from genTempGptDisk") + } + + fp, err := os.OpenFile(disk.Path, os.O_RDWR, 0) + if err != nil { + t.Fatalf("Failed to open disk %s", disk.Path) + } + + buf := make([]byte, 1024) + + for i := 0; i < 1024; i++ { + buf[i] = 0xFF + } + + // write 2MiB of 0xFF at first partition. + // Wipe should zero the first MiB + if _, err := fp.Seek(int64(disk.Partitions[1].Start), io.SeekStart); err != nil { + t.Errorf("failed seek to part1 start: %s", err) + } + + for i := 0; i < (2*int(mib))/len(buf); i++ { + if n, err := fp.Write(buf); n != len(buf) || err != nil { + t.Fatalf("failed to write 255 at %d\n", i) + } + } + fp.Close() + + if err := wipeDisk(disk); err != nil { + t.Errorf("Failed wipe of disk: %s", err) + } + + fp, err = os.OpenFile(disk.Path, os.O_RDWR, 0) + if err != nil { + t.Errorf("Failed opening %s after wipe: %s", disk.Path, err) + } + + for _, c := range [](struct { + start uint64 + size int + val byte + label string + }){ + {0, int(mib), 0x00, "disk start"}, + {disk.Partitions[1].Start, int(mib), 0x00, "part1 start"}, + {disk.Partitions[1].Start + mib, int(mib), 0xFF, "scribbled 1"}, + {disk.Size - mib, int(mib), 0x00, "disk end"}, + } { + if _, err := fp.Seek(int64(c.start), io.SeekStart); err != nil { + t.Errorf("Failed seek for %s: %s", c.label, err) + continue + } + + buf := make([]byte, c.size) + readlen, err := io.ReadFull(fp, buf) + + if err != nil { + t.Errorf("Failed read of %d from fp for %s: %s", len(buf), c.label, err) + continue + } + + if readlen != c.size { + t.Errorf("Read %d expected %d for %s: %s", readlen, c.size, c.label, err) + continue + } + + for i := 0; i < c.size; i++ { + if buf[i] != c.val { + t.Errorf("%s: %d found %d expected %d", c.label, i, buf[i], c.val) + break + } + } + } + + fp.Close() +} + func TestDeletePartition(t *testing.T) { tmpd, err := ioutil.TempDir("", "disko_test") if err != nil { @@ -306,7 +402,7 @@ func TestDeletePartition(t *testing.T) { defer os.RemoveAll(tmpd) - disk, err := genTempGptDisk(tmpd) + disk, err := genTempGptDisk(tmpd, 200*1024*1024) // nolint:gomnd if err != nil { t.Fatalf("Creation of temp disk failed: %s", err) } diff --git a/linux/system.go b/linux/system.go index d539bfc..94478af 100644 --- a/linux/system.go +++ b/linux/system.go @@ -194,7 +194,7 @@ func (ls *linuxSystem) DeletePartition(d disko.Disk, number uint) error { } func (ls *linuxSystem) Wipe(d disko.Disk) error { - if err := zeroPathStartEnd(d.Path, int64(0), int64(d.Size)); err != nil { + if err := wipeDisk(d); err != nil { return err }