restic/internal/restic/snapshot.go

221 lines
4.8 KiB
Go
Raw Normal View History

2014-12-05 20:45:49 +00:00
package restic
2014-08-04 18:47:04 +00:00
import (
2017-06-04 09:16:55 +00:00
"context"
2014-08-11 20:47:24 +00:00
"fmt"
"os/user"
2014-09-23 20:39:12 +00:00
"path/filepath"
2014-08-04 18:47:04 +00:00
"time"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/debug"
2014-08-04 18:47:04 +00:00
)
// Snapshot is the state of a resource at one point in time.
2014-08-04 18:47:04 +00:00
type Snapshot struct {
2016-08-31 18:29:54 +00:00
Time time.Time `json:"time"`
Parent *ID `json:"parent,omitempty"`
Tree *ID `json:"tree"`
Paths []string `json:"paths"`
Hostname string `json:"hostname,omitempty"`
Username string `json:"username,omitempty"`
UID uint32 `json:"uid,omitempty"`
GID uint32 `json:"gid,omitempty"`
Excludes []string `json:"excludes,omitempty"`
2016-09-13 18:12:55 +00:00
Tags []string `json:"tags,omitempty"`
Original *ID `json:"original,omitempty"`
2016-08-31 18:29:54 +00:00
id *ID // plaintext ID, used during restore
2014-08-04 18:47:04 +00:00
}
// NewSnapshot returns an initialized snapshot struct for the current user and
// time.
func NewSnapshot(paths []string, tags []string, hostname string, time time.Time) (*Snapshot, error) {
2018-01-03 21:10:20 +00:00
absPaths := make([]string, 0, len(paths))
for _, path := range paths {
p, err := filepath.Abs(path)
if err == nil {
absPaths = append(absPaths, p)
} else {
absPaths = append(absPaths, path)
2015-03-02 13:48:47 +00:00
}
2014-09-23 20:39:12 +00:00
}
2014-08-04 18:47:04 +00:00
sn := &Snapshot{
2018-01-03 21:10:20 +00:00
Paths: absPaths,
Time: time,
2017-02-10 18:37:33 +00:00
Tags: tags,
Hostname: hostname,
2014-08-04 18:47:04 +00:00
}
2017-02-10 18:37:33 +00:00
err := sn.fillUserInfo()
if err != nil {
return nil, err
2014-08-04 18:47:04 +00:00
}
return sn, nil
2014-08-04 18:47:04 +00:00
}
// LoadSnapshot loads the snapshot with the id and returns it.
2017-06-04 09:16:55 +00:00
func LoadSnapshot(ctx context.Context, repo Repository, id ID) (*Snapshot, error) {
sn := &Snapshot{id: &id}
2017-06-04 09:16:55 +00:00
err := repo.LoadJSONUnpacked(ctx, SnapshotFile, id, sn)
2014-08-04 20:46:14 +00:00
if err != nil {
return nil, err
}
return sn, nil
2014-08-04 18:47:04 +00:00
}
2014-08-11 20:47:24 +00:00
// LoadAllSnapshots returns a list of all snapshots in the repo.
2017-06-04 09:16:55 +00:00
func LoadAllSnapshots(ctx context.Context, repo Repository) (snapshots []*Snapshot, err error) {
err = repo.List(ctx, SnapshotFile, func(id ID, size int64) error {
2017-06-04 09:16:55 +00:00
sn, err := LoadSnapshot(ctx, repo, id)
2016-04-10 14:51:46 +00:00
if err != nil {
return err
2016-04-10 14:51:46 +00:00
}
snapshots = append(snapshots, sn)
return nil
})
if err != nil {
return nil, err
2016-04-10 14:51:46 +00:00
}
return snapshots, nil
2016-04-10 14:51:46 +00:00
}
func (sn Snapshot) String() string {
2016-08-19 18:50:52 +00:00
return fmt.Sprintf("<Snapshot %s of %v at %s by %s@%s>",
sn.id.Str(), sn.Paths, sn.Time, sn.Username, sn.Hostname)
2014-08-11 20:47:24 +00:00
}
2017-02-08 23:43:10 +00:00
// ID returns the snapshot's ID.
2016-08-31 18:29:54 +00:00
func (sn Snapshot) ID() *ID {
return sn.id
}
func (sn *Snapshot) fillUserInfo() error {
usr, err := user.Current()
if err != nil {
return nil
}
sn.Username = usr.Username
// set userid and groupid
sn.UID, sn.GID, err = uidGidInt(*usr)
return err
}
// AddTags adds the given tags to the snapshots tags, preventing duplicates.
// It returns true if any changes were made.
func (sn *Snapshot) AddTags(addTags []string) (changed bool) {
nextTag:
for _, add := range addTags {
for _, tag := range sn.Tags {
if tag == add {
continue nextTag
}
}
sn.Tags = append(sn.Tags, add)
changed = true
}
return
}
// RemoveTags removes the given tags from the snapshots tags and
// returns true if any changes were made.
func (sn *Snapshot) RemoveTags(removeTags []string) (changed bool) {
for _, remove := range removeTags {
for i, tag := range sn.Tags {
if tag == remove {
// https://github.com/golang/go/wiki/SliceTricks
sn.Tags[i] = sn.Tags[len(sn.Tags)-1]
sn.Tags[len(sn.Tags)-1] = ""
sn.Tags = sn.Tags[:len(sn.Tags)-1]
changed = true
break
}
}
}
return
}
func (sn *Snapshot) hasTag(tag string) bool {
for _, snTag := range sn.Tags {
if tag == snTag {
return true
}
}
return false
}
2017-07-09 07:24:02 +00:00
// HasTags returns true if the snapshot has all the tags in l.
func (sn *Snapshot) HasTags(l []string) bool {
for _, tag := range l {
if !sn.hasTag(tag) {
return false
}
}
2017-07-09 07:24:02 +00:00
return true
}
2017-07-09 07:47:41 +00:00
// HasTagList returns true if the snapshot satisfies at least one TagList,
// so there is a TagList in l for which all tags are included in sn.
func (sn *Snapshot) HasTagList(l []TagList) bool {
debug.Log("testing snapshot with tags %v against list: %v", sn.Tags, l)
if len(l) == 0 {
return true
}
for _, tags := range l {
if sn.HasTags(tags) {
2017-09-25 12:35:37 +00:00
debug.Log(" snapshot satisfies %v %v", tags, l)
2017-07-09 07:47:41 +00:00
return true
}
}
return false
}
func (sn *Snapshot) hasPath(path string) bool {
for _, snPath := range sn.Paths {
if path == snPath {
return true
}
}
return false
}
2017-07-09 07:24:02 +00:00
// HasPaths returns true if the snapshot has all of the paths.
func (sn *Snapshot) HasPaths(paths []string) bool {
for _, path := range paths {
2017-07-09 07:24:02 +00:00
if !sn.hasPath(path) {
return false
2016-05-10 19:51:56 +00:00
}
}
2017-07-09 07:24:02 +00:00
return true
}
// Snapshots is a list of snapshots.
type Snapshots []*Snapshot
// Len returns the number of snapshots in sn.
func (sn Snapshots) Len() int {
return len(sn)
}
// Less returns true iff the ith snapshot has been made after the jth.
func (sn Snapshots) Less(i, j int) bool {
return sn[i].Time.After(sn[j].Time)
}
// Swap exchanges the two snapshots.
func (sn Snapshots) Swap(i, j int) {
sn[i], sn[j] = sn[j], sn[i]
}