github.com/mckael/restic@v0.8.3/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  	err = repo.List(ctx, SnapshotFile, func(id ID, size int64) error {
    68  		sn, err := LoadSnapshot(ctx, repo, id)
    69  		if err != nil {
    70  			return err
    71  		}
    72  
    73  		snapshots = append(snapshots, sn)
    74  		return nil
    75  	})
    76  
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	return snapshots, nil
    82  }
    83  
    84  func (sn Snapshot) String() string {
    85  	return fmt.Sprintf("<Snapshot %s of %v at %s by %s@%s>",
    86  		sn.id.Str(), sn.Paths, sn.Time, sn.Username, sn.Hostname)
    87  }
    88  
    89  // ID returns the snapshot's ID.
    90  func (sn Snapshot) ID() *ID {
    91  	return sn.id
    92  }
    93  
    94  func (sn *Snapshot) fillUserInfo() error {
    95  	usr, err := user.Current()
    96  	if err != nil {
    97  		return nil
    98  	}
    99  	sn.Username = usr.Username
   100  
   101  	// set userid and groupid
   102  	sn.UID, sn.GID, err = uidGidInt(*usr)
   103  	return err
   104  }
   105  
   106  // AddTags adds the given tags to the snapshots tags, preventing duplicates.
   107  // It returns true if any changes were made.
   108  func (sn *Snapshot) AddTags(addTags []string) (changed bool) {
   109  nextTag:
   110  	for _, add := range addTags {
   111  		for _, tag := range sn.Tags {
   112  			if tag == add {
   113  				continue nextTag
   114  			}
   115  		}
   116  		sn.Tags = append(sn.Tags, add)
   117  		changed = true
   118  	}
   119  	return
   120  }
   121  
   122  // RemoveTags removes the given tags from the snapshots tags and
   123  // returns true if any changes were made.
   124  func (sn *Snapshot) RemoveTags(removeTags []string) (changed bool) {
   125  	for _, remove := range removeTags {
   126  		for i, tag := range sn.Tags {
   127  			if tag == remove {
   128  				// https://github.com/golang/go/wiki/SliceTricks
   129  				sn.Tags[i] = sn.Tags[len(sn.Tags)-1]
   130  				sn.Tags[len(sn.Tags)-1] = ""
   131  				sn.Tags = sn.Tags[:len(sn.Tags)-1]
   132  
   133  				changed = true
   134  				break
   135  			}
   136  		}
   137  	}
   138  	return
   139  }
   140  
   141  func (sn *Snapshot) hasTag(tag string) bool {
   142  	for _, snTag := range sn.Tags {
   143  		if tag == snTag {
   144  			return true
   145  		}
   146  	}
   147  	return false
   148  }
   149  
   150  // HasTags returns true if the snapshot has all the tags in l.
   151  func (sn *Snapshot) HasTags(l []string) bool {
   152  	for _, tag := range l {
   153  		if !sn.hasTag(tag) {
   154  			return false
   155  		}
   156  	}
   157  
   158  	return true
   159  }
   160  
   161  // HasTagList returns true if the snapshot satisfies at least one TagList,
   162  // so there is a TagList in l for which all tags are included in sn.
   163  func (sn *Snapshot) HasTagList(l []TagList) bool {
   164  	debug.Log("testing snapshot with tags %v against list: %v", sn.Tags, l)
   165  
   166  	if len(l) == 0 {
   167  		return true
   168  	}
   169  
   170  	for _, tags := range l {
   171  		if sn.HasTags(tags) {
   172  			debug.Log("  snapshot satisfies %v %v", tags, l)
   173  			return true
   174  		}
   175  	}
   176  
   177  	return false
   178  }
   179  
   180  func (sn *Snapshot) hasPath(path string) bool {
   181  	for _, snPath := range sn.Paths {
   182  		if path == snPath {
   183  			return true
   184  		}
   185  	}
   186  	return false
   187  }
   188  
   189  // HasPaths returns true if the snapshot has all of the paths.
   190  func (sn *Snapshot) HasPaths(paths []string) bool {
   191  	for _, path := range paths {
   192  		if !sn.hasPath(path) {
   193  			return false
   194  		}
   195  	}
   196  
   197  	return true
   198  }
   199  
   200  // Snapshots is a list of snapshots.
   201  type Snapshots []*Snapshot
   202  
   203  // Len returns the number of snapshots in sn.
   204  func (sn Snapshots) Len() int {
   205  	return len(sn)
   206  }
   207  
   208  // Less returns true iff the ith snapshot has been made after the jth.
   209  func (sn Snapshots) Less(i, j int) bool {
   210  	return sn[i].Time.After(sn[j].Time)
   211  }
   212  
   213  // Swap exchanges the two snapshots.
   214  func (sn Snapshots) Swap(i, j int) {
   215  	sn[i], sn[j] = sn[j], sn[i]
   216  }