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 }