blob: 3d817e4b415212d2e97b9bfb00deb3422a9a7864 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package util
import (
"crypto/sha256"
"encoding/hex"
"math/rand"
"os"
"syscall"
"unsafe"
"chromiumos/tast/errors"
)
const (
// 0x1277 is ioctl BLKDISCARD command.
blkdiscardIoctlCommand = 0x1277
// IOCTL error number for "Trim not supported".
ioctlNotSupportedError = 95
// TrimChunkSize is the size (in bytes) of a data chunk to trim.
TrimChunkSize = 192 * 1024
)
type trimArgs struct {
offset, size uint64
}
// RunTrim invokes ioctl trim command.
func RunTrim(f *os.File, offset, size uint64) error {
_, _, errCode := syscall.RawSyscall(
syscall.SYS_IOCTL,
uintptr(f.Fd()),
blkdiscardIoctlCommand,
uintptr(unsafe.Pointer(&trimArgs{offset: offset * TrimChunkSize, size: size})))
if errCode == ioctlNotSupportedError {
return errors.New("IOCTL trim command not supported")
}
if errCode != 0 {
return errors.Errorf("failed to execute trim command: %d", errCode)
}
return nil
}
// ZeroHash calculates sha256 has of the 0-value block.
func ZeroHash() string {
buf := make([]byte, TrimChunkSize)
return sha256String(buf)
}
// OneHash calculates sha256 has of the 1-value block.
func OneHash() string {
buf := make([]byte, TrimChunkSize)
for i := range buf {
buf[i] = 0xff
}
return sha256String(buf)
}
// CalculateCurrentHashes calculates hash values for all chunks of the given file.
func CalculateCurrentHashes(filename string, chunkCount uint64) (hashes []string, err error) {
f, err := os.Open(filename)
if err != nil {
return hashes, errors.Wrapf(err, " failed opening device: %s", filename)
}
defer f.Close()
buf := make([]byte, TrimChunkSize)
for i := uint64(0); i < chunkCount; i++ {
if n, err := f.ReadAt(buf, int64(i*TrimChunkSize)); err != nil || n != TrimChunkSize {
return hashes, errors.Wrapf(err, " error reading from: %s", filename)
}
hashes = append(hashes, sha256String(buf))
}
return hashes, nil
}
// WriteRandomData dumps random data to a given file/disk.
func WriteRandomData(filename string, chunkCount uint64) error {
f, err := os.OpenFile(filename, os.O_RDWR, 0)
if err != nil {
return errors.Wrapf(err, " failed opening device: %s", filename)
}
defer f.Close()
// Prepare a random buffer to write.
buf := make([]byte, TrimChunkSize)
if n, err := rand.Read(buf); err != nil || uint64(n) != TrimChunkSize {
return errors.Wrapf(err, " error generating random buffer of length: %d", TrimChunkSize)
}
// Write the disk partition in chunks of TrimChunkSize bytes.
for i := uint64(0); i < chunkCount; i++ {
if n, err := f.WriteAt(buf, int64(i*TrimChunkSize)); err != nil || uint64(n) != TrimChunkSize {
return errors.Wrapf(err, " error writing random data to: %s", filename)
}
}
return nil
}
func sha256String(buf []byte) string {
sha := sha256.Sum256(buf)
return hex.EncodeToString(sha[:])
}
func random(min, max int) int {
return rand.Intn(max-min) + min
}