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 }