github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/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 }