blob: 922919976ac64cd39b81d0e219a59c729f63430e [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 disk
import (
"io/ioutil"
"os"
"syscall"
"chromiumos/tast/errors"
)
// Fill creates a temporary file in a directory that fills the disk by
// allocating it, but without performing any actual IO to write the content.
func Fill(dir string, tofill uint64) (string, error) {
file, err := ioutil.TempFile(dir, "fill.*.dat")
if err != nil {
return "", errors.Wrapf(err, "failed to create temp file in %s", dir)
}
defer file.Close()
// Allocate disk space without writing content
if err := syscall.Fallocate(int(file.Fd()), 0, 0, int64(tofill)); err != nil {
return "", errors.Wrapf(err, "failed to allocate %v bytes in %s", tofill, file.Name())
}
return file.Name(), nil
}
// FillUntil reates a temporary file in a directory that fills the disk until
// less than remaining bytes are available.
func FillUntil(dir string, remaining uint64) (string, error) {
freeSpace, err := FreeSpace(dir)
if err != nil {
return "", errors.Wrapf(err, "failed to read free space in %s", dir)
}
if freeSpace < remaining {
return "", errors.Errorf("insufficient free space; %v < %v", freeSpace, remaining)
}
tofill := freeSpace - remaining
return Fill(dir, tofill)
}
// Refiller maintains the temporary fill file for you, so you can easily
// (re-)adjust the disk space.
type Refiller struct {
// dir is the directory to put the temporary file in.
dir string
// filePath is the path to the temporary file, or empty if there isn't one.
filePath string
}
// NewRefiller creates a new refiller. `dir` is the directory to put the
// temporary file in.
//
// Example:
//
// refiller := NewRefiller(...)
// defer func() {
// if err := refiller.Close(); err != nil {
// s.Error(...)
// }
// }()
// refiller.RefillUntil(...)
// ...
// refiller.RefillUntil(...)
// ...
func NewRefiller(dir string) Refiller {
return Refiller{dir: dir, filePath: ""}
}
// Close removes the temporary file if there is one. You can call it as many
// times as you want, and you should also call it at the end of your tast to
// clean things up.
func (r *Refiller) Close() error {
if r.filePath != "" {
if err := os.Remove(r.filePath); err != nil {
return errors.Wrapf(err, "Unable to remove the temporary file %v", r.filePath)
}
r.filePath = ""
}
return nil
}
// Refill removes the temporary file previously created, and then fill the
// disk. See `Fill()`.
func (r *Refiller) Refill(tofill uint64) error {
return r.refillImpl(Fill, tofill)
}
// RefillUntil removes the temporary file previously created, and then fill the
// disk. See `FillUntil()`.
func (r *Refiller) RefillUntil(remaining uint64) error {
return r.refillImpl(FillUntil, remaining)
}
func (r *Refiller) refillImpl(fillFunc func(string, uint64) (string, error), bytes uint64) error {
if err := r.Close(); err != nil {
return err
}
filePath, err := fillFunc(r.dir, bytes)
if err != nil {
return err
}
r.filePath = filePath
return nil
}