github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/md_id_journal.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libkbfs 6 7 import ( 8 "reflect" 9 10 "github.com/keybase/client/go/kbfs/ioutil" 11 "github.com/keybase/client/go/kbfs/kbfscodec" 12 "github.com/keybase/client/go/kbfs/kbfsmd" 13 "github.com/keybase/go-codec/codec" 14 "github.com/pkg/errors" 15 ) 16 17 // An mdIDJournal wraps a diskJournal to provide a persistent list of 18 // MdIDs (with possible other fields in the future) with sequential 19 // Revisions for a single branch. 20 // 21 // Like diskJournal, this type assumes that the directory passed into 22 // makeMdIDJournal isn't used by anything else, and that all 23 // synchronization is done at a higher level. 24 // 25 // TODO: Write unit tests for this. For now, we're relying on 26 // md_journal.go's unit tests. 27 type mdIDJournal struct { 28 j *diskJournal 29 } 30 31 // An mdIDJournalEntry is an MdID and a boolean describing whether 32 // this entry was the result of a local squash. Make sure that new 33 // fields don't depend on the ID or `isLocalSquash`, as the mdJournal 34 // may change these when converting to a branch. Note that 35 // `isLocalSquash` may only be true for entries in a continuous prefix 36 // of the id journal; once there is one entry with `isLocalSquash = 37 // false`, it will be the same in all the remaining entries. 38 type mdIDJournalEntry struct { 39 ID kbfsmd.ID 40 // IsLocalSquash is true when this MD is the result of 41 // squashing some other local MDs. 42 IsLocalSquash bool `codec:",omitempty"` 43 // WKBNew is true when the writer key bundle for this MD is 44 // new and has to be pushed to the server. This is always 45 // false for MDv2. 46 WKBNew bool `codec:",omitempty"` 47 // RKBNew is true when the reader key bundle for this MD is 48 // new and has to be pushed to the server. This is always 49 // false for MDv2. 50 RKBNew bool `codec:",omitempty"` 51 52 codec.UnknownFieldSetHandler 53 } 54 55 func makeMdIDJournal(codec kbfscodec.Codec, dir string) (mdIDJournal, error) { 56 j, err := 57 makeDiskJournal(codec, dir, reflect.TypeOf(mdIDJournalEntry{})) 58 if err != nil { 59 return mdIDJournal{}, err 60 } 61 return mdIDJournal{j}, nil 62 } 63 64 func ordinalToRevision(o journalOrdinal) (kbfsmd.Revision, error) { 65 r := kbfsmd.Revision(o) 66 if r < kbfsmd.RevisionInitial { 67 return kbfsmd.RevisionUninitialized, errors.Errorf( 68 "Cannot convert ordinal %s to a kbfsmd.Revision", o) 69 } 70 return r, nil 71 } 72 73 func revisionToOrdinal(r kbfsmd.Revision) (journalOrdinal, error) { 74 if r < kbfsmd.RevisionInitial { 75 return journalOrdinal(0), errors.Errorf( 76 "Cannot convert revision %s to an ordinal", r) 77 } 78 return journalOrdinal(r), nil 79 } 80 81 // TODO: Consider caching the values returned by the read functions 82 // below in memory. 83 84 func (j mdIDJournal) readEarliestRevision() (kbfsmd.Revision, error) { 85 o, err := j.j.readEarliestOrdinal() 86 if ioutil.IsNotExist(err) { 87 return kbfsmd.RevisionUninitialized, nil 88 } else if err != nil { 89 return kbfsmd.RevisionUninitialized, err 90 } 91 return ordinalToRevision(o) 92 } 93 94 func (j mdIDJournal) readLatestRevision() (kbfsmd.Revision, error) { 95 o, err := j.j.readLatestOrdinal() 96 if ioutil.IsNotExist(err) { 97 return kbfsmd.RevisionUninitialized, nil 98 } else if err != nil { 99 return kbfsmd.RevisionUninitialized, err 100 } 101 return ordinalToRevision(o) 102 } 103 104 func (j mdIDJournal) writeLatestRevision(r kbfsmd.Revision) error { 105 o, err := revisionToOrdinal(r) 106 if err != nil { 107 return err 108 } 109 return j.j.writeLatestOrdinal(o) 110 } 111 112 func (j mdIDJournal) readJournalEntry(r kbfsmd.Revision) ( 113 mdIDJournalEntry, error) { 114 o, err := revisionToOrdinal(r) 115 if err != nil { 116 return mdIDJournalEntry{}, err 117 } 118 e, err := j.j.readJournalEntry(o) 119 if err != nil { 120 return mdIDJournalEntry{}, err 121 } 122 123 return e.(mdIDJournalEntry), nil 124 } 125 126 // All functions below are public functions. 127 128 func (j mdIDJournal) length() uint64 { 129 return j.j.length() 130 } 131 132 func (j mdIDJournal) end() (kbfsmd.Revision, error) { 133 last, err := j.readLatestRevision() 134 if err != nil { 135 return kbfsmd.RevisionUninitialized, err 136 } 137 if last == kbfsmd.RevisionUninitialized { 138 return kbfsmd.RevisionUninitialized, nil 139 } 140 141 return last + 1, nil 142 } 143 144 func (j mdIDJournal) getEarliestEntry() ( 145 entry mdIDJournalEntry, exists bool, err error) { 146 earliestRevision, err := j.readEarliestRevision() 147 if err != nil { 148 return mdIDJournalEntry{}, false, err 149 } else if earliestRevision == kbfsmd.RevisionUninitialized { 150 return mdIDJournalEntry{}, false, nil 151 } 152 entry, err = j.readJournalEntry(earliestRevision) 153 if err != nil { 154 return mdIDJournalEntry{}, false, err 155 } 156 return entry, true, err 157 } 158 159 func (j mdIDJournal) getLatestEntry() ( 160 entry mdIDJournalEntry, exists bool, err error) { 161 latestRevision, err := j.readLatestRevision() 162 if err != nil { 163 return mdIDJournalEntry{}, false, err 164 } else if latestRevision == kbfsmd.RevisionUninitialized { 165 return mdIDJournalEntry{}, false, nil 166 } 167 entry, err = j.readJournalEntry(latestRevision) 168 if err != nil { 169 return mdIDJournalEntry{}, false, err 170 } 171 return entry, true, err 172 } 173 174 func (j mdIDJournal) getEntryRange(start, stop kbfsmd.Revision) ( 175 kbfsmd.Revision, []mdIDJournalEntry, error) { 176 earliestRevision, err := j.readEarliestRevision() 177 if err != nil { 178 return kbfsmd.RevisionUninitialized, nil, err 179 } else if earliestRevision == kbfsmd.RevisionUninitialized { 180 return kbfsmd.RevisionUninitialized, nil, nil 181 } 182 183 latestRevision, err := j.readLatestRevision() 184 if err != nil { 185 return kbfsmd.RevisionUninitialized, nil, err 186 } else if latestRevision == kbfsmd.RevisionUninitialized { 187 return kbfsmd.RevisionUninitialized, nil, nil 188 } 189 190 if start < earliestRevision { 191 start = earliestRevision 192 } 193 194 if stop > latestRevision { 195 stop = latestRevision 196 } 197 198 if stop < start { 199 return kbfsmd.RevisionUninitialized, nil, nil 200 } 201 202 var entries []mdIDJournalEntry 203 for i := start; i <= stop; i++ { 204 entry, err := j.readJournalEntry(i) 205 if err != nil { 206 return kbfsmd.RevisionUninitialized, nil, err 207 } 208 entries = append(entries, entry) 209 } 210 return start, entries, nil 211 } 212 213 func (j mdIDJournal) replaceHead(entry mdIDJournalEntry) error { 214 o, err := j.j.readLatestOrdinal() 215 if err != nil { 216 return err 217 } 218 return j.j.writeJournalEntry(o, entry) 219 } 220 221 func (j mdIDJournal) append(r kbfsmd.Revision, entry mdIDJournalEntry) error { 222 o, err := revisionToOrdinal(r) 223 if err != nil { 224 return err 225 } 226 _, err = j.j.appendJournalEntry(&o, entry) 227 return err 228 } 229 230 func (j mdIDJournal) removeEarliest() (empty bool, err error) { 231 return j.j.removeEarliest() 232 } 233 234 func (j mdIDJournal) clear() error { 235 return j.j.clear() 236 } 237 238 func (j mdIDJournal) clearFrom(revision kbfsmd.Revision) error { 239 earliestRevision, err := j.readEarliestRevision() 240 if err != nil { 241 return err 242 } 243 244 if revision < earliestRevision { 245 return errors.Errorf("Cannot call clearFrom with revision %s < %s", 246 revision, earliestRevision) 247 } 248 249 if revision == earliestRevision { 250 return j.clear() 251 } 252 253 latestRevision, err := j.readLatestRevision() 254 if err != nil { 255 return err 256 } 257 258 err = j.writeLatestRevision(revision - 1) 259 if err != nil { 260 return err 261 } 262 263 o, err := revisionToOrdinal(revision) 264 if err != nil { 265 return err 266 } 267 268 latestOrdinal, err := revisionToOrdinal(latestRevision) 269 if err != nil { 270 return err 271 } 272 273 for ; o <= latestOrdinal; o++ { 274 p := j.j.journalEntryPath(o) 275 err = ioutil.Remove(p) 276 if err != nil { 277 return err 278 } 279 } 280 281 return nil 282 } 283 284 // Note that since diskJournal.move takes a pointer receiver, so must 285 // this. 286 func (j *mdIDJournal) move(newDir string) (oldDir string, err error) { 287 return j.j.move(newDir) 288 }