blob: 9b2bcca830581da1b4f70dc67be88b7526c1b275 [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 filesnapshot provides functions that store/restore the snapshot (i.e. the content) of important files during integration test.
package filesnapshot
import (
"io/ioutil"
"os"
"path/filepath"
"syscall"
"chromiumos/tast/errors"
)
// snapshotValue defines the a file snapshot, including the file content, permission, and its urser and group IDs.
type snapshotValue struct {
content []byte
mode os.FileMode
uid, gid int
}
// Snapshot mentions the lookup table from the filename to the corresponding stored snapshot, and it supports the operations that are defined below.
type Snapshot struct {
table map[string]snapshotValue
}
// NewSnapshot creates a new Snapshot of which lookup table is initialized.
func NewSnapshot() *Snapshot {
return &Snapshot{make(map[string]snapshotValue)}
}
// Save reads the snapshot of filename, and stores it associated with the filename. If the snapshot is been saved before, overrides it.
func (s *Snapshot) Save(filename string) error {
if !filepath.IsAbs(filename) {
return errors.New("not an absolute path")
}
content, err := ioutil.ReadFile(filename)
if err != nil {
return errors.Wrap(err, "failed to read file")
}
stat, err := os.Stat(filename)
if err != nil {
return errors.Wrap(err, "failed to get file stat")
}
rawStat, ok := stat.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("failed to get raw stat structure")
}
s.table[filename] = snapshotValue{content, stat.Mode(), int(rawStat.Uid), int(rawStat.Gid)}
return nil
}
// Restore restores the snapshot of "filename". Note that the snapshot entry is not removed after the operation.
func (s *Snapshot) Restore(filename string) error {
val, ok := s.table[filename]
if !ok {
return errors.Errorf("snapshot of path %s not found", filename)
}
if err := ioutil.WriteFile(filename, val.content, val.mode); err != nil {
return errors.Wrap(err, "failed to restore file content")
}
// Always set the permission again; necessary when the file exist already when calling WriteFile.
if err := os.Chmod(filename, val.mode); err != nil {
return errors.Wrap(err, "failed to restore file permission")
}
if err := os.Chown(filename, val.uid, val.gid); err != nil {
return errors.Wrap(err, "failed to restore file ownership")
}
return nil
}
// Remove removes the entry of filename from the snapshot lookup table; if the snapshot doesn't exist, performs no-ops.
func (s *Snapshot) Remove(filename string) {
delete(s.table, filename)
}
// Stash stores the snapshot and delete the file.
func (s *Snapshot) Stash(filename string) error {
if err := s.Save(filename); err != nil {
return errors.Wrap(err, "failed to take a snapshot")
}
if err := os.Remove(filename); err != nil {
return errors.Wrap(err, "failed to remove the file")
}
return nil
}
// Pop restores and the file content and delete the snapshot for restoration as well.
func (s *Snapshot) Pop(filename string) error {
if err := s.Restore(filename); err != nil {
return errors.Wrap(err, "failed to restore from snapshot")
}
s.Remove(filename)
return nil
}