github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/restic/snapshot.go (about)

     1  package restic
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os/user"
     7  	"path/filepath"
     8  	"time"
     9  
    10  	"github.com/restic/restic/internal/debug"
    11  )
    12  
    13  // Snapshot is the state of a resource at one point in time.
    14  type Snapshot struct {
    15  	Time     time.Time `json:"time"`
    16  	Parent   *ID       `json:"parent,omitempty"`
    17  	Tree     *ID       `json:"tree"`
    18  	Paths    []string  `json:"paths"`
    19  	Hostname string    `json:"hostname,omitempty"`
    20  	Username string    `json:"username,omitempty"`
    21  	UID      uint32    `json:"uid,omitempty"`
    22  	GID      uint32    `json:"gid,omitempty"`
    23  	Excludes []string  `json:"excludes,omitempty"`
    24  	Tags     []string  `json:"tags,omitempty"`
    25  	Original *ID       `json:"original,omitempty"`
    26  
    27  	id *ID // plaintext ID, used during restore
    28  }
    29  
    30  // NewSnapshot returns an initialized snapshot struct for the current user and
    31  // time.
    32  func NewSnapshot(paths []string, tags []string, hostname string, time time.Time) (*Snapshot, error) {
    33  	for i, path := range paths {
    34  		if p, err := filepath.Abs(path); err != nil {
    35  			paths[i] = p
    36  		}
    37  	}
    38  
    39  	sn := &Snapshot{
    40  		Paths:    paths,
    41  		Time:     time,
    42  		Tags:     tags,
    43  		Hostname: hostname,
    44  	}
    45  
    46  	err := sn.fillUserInfo()
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	return sn, nil
    52  }
    53  
    54  // LoadSnapshot loads the snapshot with the id and returns it.
    55  func LoadSnapshot(ctx context.Context, repo Repository, id ID) (*Snapshot, error) {
    56  	sn := &Snapshot{id: &id}
    57  	err := repo.LoadJSONUnpacked(ctx, SnapshotFile, id, sn)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	return sn, nil
    63  }
    64  
    65  // LoadAllSnapshots returns a list of all snapshots in the repo.
    66  func LoadAllSnapshots(ctx context.Context, repo Repository) (snapshots []*Snapshot, err error) {
    67  	for id := range repo.List(ctx, SnapshotFile) {
    68  		sn, err := LoadSnapshot(ctx, repo, id)
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  
    73  		snapshots = append(snapshots, sn)
    74  	}
    75  	return
    76  }
    77  
    78  func (sn Snapshot) String() string {
    79  	return fmt.Sprintf("<Snapshot %s of %v at %s by %s@%s>",
    80  		sn.id.Str(), sn.Paths, sn.Time, sn.Username, sn.Hostname)
    81  }
    82  
    83  // ID returns the snapshot's ID.
    84  func (sn Snapshot) ID() *ID {
    85  	return sn.id
    86  }
    87  
    88  func (sn *Snapshot) fillUserInfo() error {
    89  	usr, err := user.Current()
    90  	if err != nil {
    91  		return nil
    92  	}
    93  	sn.Username = usr.Username
    94  
    95  	// set userid and groupid
    96  	sn.UID, sn.GID, err = uidGidInt(*usr)
    97  	return err
    98  }
    99  
   100  // AddTags adds the given tags to the snapshots tags, preventing duplicates.
   101  // It returns true if any changes were made.
   102  func (sn *Snapshot) AddTags(addTags []string) (changed bool) {
   103  nextTag:
   104  	for _, add := range addTags {
   105  		for _, tag := range sn.Tags {
   106  			if tag == add {
   107  				continue nextTag
   108  			}
   109  		}
   110  		sn.Tags = append(sn.Tags, add)
   111  		changed = true
   112  	}
   113  	return
   114  }
   115  
   116  // RemoveTags removes the given tags from the snapshots tags and
   117  // returns true if any changes were made.
   118  func (sn *Snapshot) RemoveTags(removeTags []string) (changed bool) {
   119  	for _, remove := range removeTags {
   120  		for i, tag := range sn.Tags {
   121  			if tag == remove {
   122  				// https://github.com/golang/go/wiki/SliceTricks
   123  				sn.Tags[i] = sn.Tags[len(sn.Tags)-1]
   124  				sn.Tags[len(sn.Tags)-1] = ""
   125  				sn.Tags = sn.Tags[:len(sn.Tags)-1]
   126  
   127  				changed = true
   128  				break
   129  			}
   130  		}
   131  	}
   132  	return
   133  }
   134  
   135  func (sn *Snapshot) hasTag(tag string) bool {
   136  	for _, snTag := range sn.Tags {
   137  		if tag == snTag {
   138  			return true
   139  		}
   140  	}
   141  	return false
   142  }
   143  
   144  // HasTags returns true if the snapshot has all the tags in l.
   145  func (sn *Snapshot) HasTags(l []string) bool {
   146  	for _, tag := range l {
   147  		if !sn.hasTag(tag) {
   148  			return false
   149  		}
   150  	}
   151  
   152  	return true
   153  }
   154  
   155  // HasTagList returns true if the snapshot satisfies at least one TagList,
   156  // so there is a TagList in l for which all tags are included in sn.
   157  func (sn *Snapshot) HasTagList(l []TagList) bool {
   158  	debug.Log("testing snapshot with tags %v against list: %v", sn.Tags, l)
   159  
   160  	if len(l) == 0 {
   161  		return true
   162  	}
   163  
   164  	for _, tags := range l {
   165  		if sn.HasTags(tags) {
   166  			debug.Log("  snapshot satisfies %v %v", tags, l)
   167  			return true
   168  		}
   169  	}
   170  
   171  	return false
   172  }
   173  
   174  func (sn *Snapshot) hasPath(path string) bool {
   175  	for _, snPath := range sn.Paths {
   176  		if path == snPath {
   177  			return true
   178  		}
   179  	}
   180  	return false
   181  }
   182  
   183  // HasPaths returns true if the snapshot has all of the paths.
   184  func (sn *Snapshot) HasPaths(paths []string) bool {
   185  	for _, path := range paths {
   186  		if !sn.hasPath(path) {
   187  			return false
   188  		}
   189  	}
   190  
   191  	return true
   192  }
   193  
   194  // Snapshots is a list of snapshots.
   195  type Snapshots []*Snapshot
   196  
   197  // Len returns the number of snapshots in sn.
   198  func (sn Snapshots) Len() int {
   199  	return len(sn)
   200  }
   201  
   202  // Less returns true iff the ith snapshot has been made after the jth.
   203  func (sn Snapshots) Less(i, j int) bool {
   204  	return sn[i].Time.After(sn[j].Time)
   205  }
   206  
   207  // Swap exchanges the two snapshots.
   208  func (sn Snapshots) Swap(i, j int) {
   209  	sn[i], sn[j] = sn[j], sn[i]
   210  }