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
5package libkbfs
6
7import (
8	"path/filepath"
9	"time"
10
11	"github.com/keybase/client/go/kbfs/data"
12	"github.com/keybase/client/go/kbfs/idutil"
13	"github.com/keybase/client/go/kbfs/ioutil"
14	"github.com/keybase/client/go/kbfs/kbfscodec"
15	"github.com/keybase/client/go/kbfs/kbfscrypto"
16	"github.com/keybase/client/go/kbfs/kbfsmd"
17	"github.com/keybase/client/go/kbfs/tlf"
18	"github.com/keybase/client/go/logger"
19	"github.com/keybase/client/go/protocol/keybase1"
20	"github.com/pkg/errors"
21	"golang.org/x/net/context"
22)
23
24// ImmutableBareRootMetadata is a thin wrapper around a
25// BareRootMetadata and a kbfsmd.ExtraMetadata that takes ownership of it
26// and does not ever modify it again. Thus, its MdID can be calculated
27// and stored along with a local timestamp. ImmutableBareRootMetadata
28// objects can be assumed to never alias a (modifiable)
29// BareRootMetadata.
30//
31// Note that kbfsmd.MakeID() on an ImmutableBareRootMetadata will
32// compute the wrong result, since anonymous fields of interface type
33// are not encoded inline by the codec. Use
34// kbfsmd.MakeID(ibrmd.BareRootMetadata) instead.
35//
36// TODO: Move this to bare_root_metadata.go if it's used in more
37// places.
38type ImmutableBareRootMetadata struct {
39	kbfsmd.RootMetadata
40	extra          kbfsmd.ExtraMetadata
41	mdID           kbfsmd.ID
42	localTimestamp time.Time
43}
44
45// MakeImmutableBareRootMetadata makes a new ImmutableBareRootMetadata
46// from the given BareRootMetadata and its corresponding MdID.
47func MakeImmutableBareRootMetadata(
48	rmd kbfsmd.RootMetadata, extra kbfsmd.ExtraMetadata, mdID kbfsmd.ID,
49	localTimestamp time.Time) ImmutableBareRootMetadata {
50	if mdID == (kbfsmd.ID{}) {
51		panic("zero mdID passed to MakeImmutableBareRootMetadata")
52	}
53	return ImmutableBareRootMetadata{rmd, extra, mdID, localTimestamp}
54}
55
56// MakeBareTlfHandleWithExtra makes a BareTlfHandle for this
57// ImmutableBareRootMetadata. Should be used only by servers and MDOps.
58func (ibrmd ImmutableBareRootMetadata) MakeBareTlfHandleWithExtra() (
59	tlf.Handle, error) {
60	return ibrmd.RootMetadata.MakeBareTlfHandle(ibrmd.extra)
61}
62
63// mdJournal stores a single ordered list of metadata IDs for a (TLF,
64// user, device) tuple, along with the associated metadata objects, in
65// flat files on disk in a directory. The directory may be shared with
66// other things, but it is assumed that any subdirectories created by
67// mdJournal is not used by anything else.
68//
69// The directory layout looks like:
70//
71// dir/md_journal/EARLIEST
72// dir/md_journal/LATEST
73// dir/md_journal/0...001
74// dir/md_journal/0...002
75// dir/md_journal/0...fff
76// dir/mds/0100/0...01/data
77// dir/mds/0100/0...01/info.json
78// ...
79// dir/mds/01ff/f...ff/data
80// dir/mds/01ff/f...ff/info.json
81// dir/wkbv3/0100...01
82// ...
83// dir/wkbv3/0100...ff
84// dir/rkbv3/0100...01
85// ...
86// dir/rkbv3/0100...ff
87//
88// There's a single journal subdirectory; the journal ordinals are
89// just Revisions, and the journal entries are just MdIDs.
90//
91// The Metadata objects are stored separately in dir/mds. Each MD has
92// its own subdirectory with its ID truncated to 17 bytes (34
93// characters) as a name. The MD subdirectories are splayed over (# of
94// possible hash types) * 256 subdirectories -- one byte for the hash
95// type (currently only one) plus the first byte of the hash data --
96// using the first four characters of the name to keep the number of
97// directories in dir itself to a manageable number, similar to git.
98// Each block directory has data, which is the raw MD data that should
99// hash to the MD ID, and info.json, which contains the version and
100// timestamp info for that MD. Future versions of the journal might
101// add more files to this directory; if any code is written to move
102// MDs around, it should be careful to preserve any unknown files in
103// an MD directory.
104//
105// Writer (reader) key bundles for V3 metadata objects are stored
106// separately in dir/wkbv3 (dir/rkbv3). The number of bundles is
107// small, so no need to splay them.
108//
109// TODO: Garbage-collect unreferenced key bundles.
110//
111// The maximum number of characters added to the root dir by an MD
112// journal is 50:
113//
114//   /mds/01ff/f...(30 characters total)...ff/info.json
115//
116// This covers even the temporary files created in convertToBranch and
117// resolveAndClear, which create paths like
118//
119//   /md_journal123456789/0...(16 characters total)...001
120//
121// which have only 37 characters.
122//
123// mdJournal is not goroutine-safe, so any code that uses it must
124// guarantee that only one goroutine at a time calls its functions.
125type mdJournal struct {
126	// key is assumed to be the VerifyingKey of a device owned by
127	// uid, and both uid and key are assumed constant for the
128	// lifetime of this object.
129	uid keybase1.UID
130	key kbfscrypto.VerifyingKey
131
132	codec          kbfscodec.Codec
133	crypto         cryptoPure
134	clock          Clock
135	teamMemChecker kbfsmd.TeamMembershipChecker
136	osg            idutil.OfflineStatusGetter
137	tlfID          tlf.ID
138	mdVer          kbfsmd.MetadataVer
139	dir            string
140	overrideTlfID  tlf.ID
141
142	log      logger.Logger
143	deferLog logger.Logger
144
145	j mdIDJournal
146
147	// branchID is the kbfsmd.BranchID that every MD in the journal is set
148	// to, except for when it is kbfsmd.PendingLocalSquashBranchID, in which
149	// case the journal is a bunch of MDs with a null branchID
150	// followed by a bunch of MDs with bid =
151	// kbfsmd.PendingLocalSquashBranchID.
152	//
153	// branchID doesn't need to be persisted, even if the journal
154	// becomes empty, since on a restart the branch ID is retrieved
155	// from the server (via GetUnmergedForTLF).
156	branchID kbfsmd.BranchID
157
158	// Set only when the journal becomes empty due to
159	// flushing. This doesn't need to be persisted for the same
160	// reason as branchID.
161	lastMdID kbfsmd.ID
162
163	// journalID is a unique identifier for this journal since the
164	// last time, renewed the last time it was completely cleared.  It
165	// must be persisted in a file, since it is referenced
166	// persistently in the block journal.  It's used to help clear md
167	// markers in the block journal atomically.
168	journalID kbfsmd.ID
169}
170
171func makeMDJournalWithIDJournal(
172	ctx context.Context, uid keybase1.UID, key kbfscrypto.VerifyingKey,
173	codec kbfscodec.Codec, crypto cryptoPure, clock Clock,
174	teamMemChecker kbfsmd.TeamMembershipChecker, osg idutil.OfflineStatusGetter,
175	tlfID tlf.ID, mdVer kbfsmd.MetadataVer, dir string, idJournal mdIDJournal,
176	log logger.Logger, overrideTlfID tlf.ID) (*mdJournal, error) {
177	if uid == keybase1.UID("") {
178		return nil, errors.New("Empty user")
179	}
180	if key == (kbfscrypto.VerifyingKey{}) {
181		return nil, errors.New("Empty verifying key")
182	}
183
184	deferLog := log.CloneWithAddedDepth(1)
185	journal := mdJournal{
186		uid:            uid,
187		key:            key,
188		codec:          codec,
189		crypto:         crypto,
190		clock:          clock,
191		teamMemChecker: teamMemChecker,
192		osg:            osg,
193		tlfID:          tlfID,
194		mdVer:          mdVer,
195		dir:            dir,
196		overrideTlfID:  overrideTlfID,
197		log:            log,
198		deferLog:       deferLog,
199		j:              idJournal,
200	}
201
202	_, earliest, _, _, err := journal.getEarliestWithExtra(ctx, false)
203	if err != nil {
204		return nil, err
205	}
206
207	latest, err := journal.getLatest(ctx, false)
208	if err != nil {
209		return nil, err
210	}
211
212	if (earliest == nil) != (latest == ImmutableBareRootMetadata{}) {
213		return nil, errors.Errorf("has earliest=%t != has latest=%t",
214			earliest != nil,
215			latest != ImmutableBareRootMetadata{})
216	}
217
218	if earliest != nil {
219		if earliest.BID() != latest.BID() &&
220			!(earliest.BID() == kbfsmd.NullBranchID &&
221				latest.BID() == kbfsmd.PendingLocalSquashBranchID) {
222			return nil, errors.Errorf(
223				"earliest.BID=%s != latest.BID=%s",
224				earliest.BID(), latest.BID())
225		}
226		log.CDebugf(ctx, "Initializing with branch ID %s", latest.BID())
227		journal.branchID = latest.BID()
228	}
229
230	return &journal, nil
231}
232
233func mdJournalPath(dir string) string {
234	return filepath.Join(dir, "md_journal")
235}
236
237func makeMDJournal(
238	ctx context.Context, uid keybase1.UID, key kbfscrypto.VerifyingKey,
239	codec kbfscodec.Codec, crypto cryptoPure, clock Clock,
240	teamMemChecker kbfsmd.TeamMembershipChecker, osg idutil.OfflineStatusGetter,
241	tlfID tlf.ID, mdVer kbfsmd.MetadataVer, dir string,
242	log logger.Logger, overrideTlfID tlf.ID) (*mdJournal, error) {
243	journalDir := mdJournalPath(dir)
244	idJournal, err := makeMdIDJournal(codec, journalDir)
245	if err != nil {
246		return nil, err
247	}
248	return makeMDJournalWithIDJournal(
249		ctx, uid, key, codec, crypto, clock, teamMemChecker, osg, tlfID, mdVer,
250		dir, idJournal, log, overrideTlfID)
251}
252
253// The functions below are for building various paths.
254
255func (j mdJournal) mdsPath() string {
256	return filepath.Join(j.dir, "mds")
257}
258
259func (j mdJournal) writerKeyBundlesV3Path() string {
260	return filepath.Join(j.dir, "wkbv3")
261}
262
263func (j mdJournal) readerKeyBundlesV3Path() string {
264	return filepath.Join(j.dir, "rkbv3")
265}
266
267// The final components of the paths below are truncated to 34
268// characters, which corresponds to 16 random bytes (since the first
269// byte is a hash type) or 128 random bits, which means that the
270// expected number of MDs generated before getting a path collision is
271// 2^64 (see
272// https://en.wikipedia.org/wiki/Birthday_problem#Cast_as_a_collision_problem
273// ). The full ID can be recovered just by hashing the data again with
274// the same hash type.
275
276func (j mdJournal) writerKeyBundleV3Path(id kbfsmd.TLFWriterKeyBundleID) string {
277	idStr := id.String()
278	return filepath.Join(j.writerKeyBundlesV3Path(), idStr[:34])
279}
280
281func (j mdJournal) readerKeyBundleV3Path(id kbfsmd.TLFReaderKeyBundleID) string {
282	idStr := id.String()
283	return filepath.Join(j.readerKeyBundlesV3Path(), idStr[:34])
284}
285
286func (j mdJournal) mdJournalDirs() []string {
287	return []string{
288		mdJournalPath(j.dir), j.mdsPath(),
289		j.writerKeyBundlesV3Path(), j.readerKeyBundlesV3Path(),
290	}
291}
292
293func (j mdJournal) mdPath(id kbfsmd.ID) string {
294	idStr := id.String()
295	return filepath.Join(j.mdsPath(), idStr[:4], idStr[4:34])
296}
297
298func (j mdJournal) mdDataPath(id kbfsmd.ID) string {
299	return filepath.Join(j.mdPath(id), "data")
300}
301
302func (j mdJournal) mdInfoPath(id kbfsmd.ID) string {
303	return filepath.Join(j.mdPath(id), "info.json")
304}
305
306// mdInfo is the structure stored in mdInfoPath(id).
307//
308// TODO: Handle unknown fields? We'd have to build a handler for this,
309// since the Go JSON library doesn't support it natively.
310type mdInfo struct {
311	Timestamp time.Time
312	Version   kbfsmd.MetadataVer
313}
314
315func (j mdJournal) getMDInfo(id kbfsmd.ID) (time.Time, kbfsmd.MetadataVer, error) {
316	var info mdInfo
317	err := ioutil.DeserializeFromJSONFile(j.mdInfoPath(id), &info)
318	if err != nil {
319		return time.Time{}, kbfsmd.MetadataVer(-1), err
320	}
321
322	return info.Timestamp, info.Version, nil
323}
324
325// putMDInfo assumes that the parent directory of j.mdInfoPath(id)
326// (which is j.mdPath(id)) has already been created.
327func (j mdJournal) putMDInfo(
328	id kbfsmd.ID, timestamp time.Time, version kbfsmd.MetadataVer) error {
329	info := mdInfo{timestamp, version}
330	return ioutil.SerializeToJSONFile(info, j.mdInfoPath(id))
331}
332
333// getExtraMetadata gets the extra metadata corresponding to the given
334// IDs, if any, after checking them.
335func (j mdJournal) getExtraMetadata(
336	wkbID kbfsmd.TLFWriterKeyBundleID, rkbID kbfsmd.TLFReaderKeyBundleID,
337	wkbNew, rkbNew bool) (kbfsmd.ExtraMetadata, error) {
338	if (wkbID == kbfsmd.TLFWriterKeyBundleID{}) !=
339		(rkbID == kbfsmd.TLFReaderKeyBundleID{}) {
340		return nil, errors.Errorf(
341			"wkbID is empty (%t) != rkbID is empty (%t)",
342			wkbID == kbfsmd.TLFWriterKeyBundleID{},
343			rkbID == kbfsmd.TLFReaderKeyBundleID{})
344	}
345
346	if wkbID == (kbfsmd.TLFWriterKeyBundleID{}) {
347		return nil, nil
348	}
349
350	wkb, err := kbfsmd.DeserializeTLFWriterKeyBundleV3(
351		j.codec, j.writerKeyBundleV3Path(wkbID))
352	if err != nil {
353		return nil, err
354	}
355
356	err = kbfsmd.CheckWKBID(j.codec, wkbID, wkb)
357	if err != nil {
358		return nil, err
359	}
360
361	rkb, err := kbfsmd.DeserializeTLFReaderKeyBundleV3(
362		j.codec, j.readerKeyBundleV3Path(rkbID))
363	if err != nil {
364		return nil, err
365	}
366
367	err = kbfsmd.CheckRKBID(j.codec, rkbID, rkb)
368	if err != nil {
369		return nil, err
370	}
371
372	return kbfsmd.NewExtraMetadataV3(wkb, rkb, wkbNew, rkbNew), nil
373}
374
375func (j mdJournal) putExtraMetadata(rmd kbfsmd.RootMetadata, extra kbfsmd.ExtraMetadata) (
376	wkbNew, rkbNew bool, err error) {
377	wkbID := rmd.GetTLFWriterKeyBundleID()
378	rkbID := rmd.GetTLFReaderKeyBundleID()
379
380	if extra == nil {
381		if wkbID != (kbfsmd.TLFWriterKeyBundleID{}) {
382			panic(errors.Errorf("unexpected non-nil wkbID %s", wkbID))
383		}
384		if rkbID != (kbfsmd.TLFReaderKeyBundleID{}) {
385			panic(errors.Errorf("unexpected non-nil rkbID %s", rkbID))
386		}
387		return false, false, nil
388	}
389
390	if wkbID == (kbfsmd.TLFWriterKeyBundleID{}) {
391		panic("writer key bundle ID is empty")
392	}
393
394	if rkbID == (kbfsmd.TLFReaderKeyBundleID{}) {
395		panic("reader key bundle ID is empty")
396	}
397
398	extraV3, ok := extra.(*kbfsmd.ExtraMetadataV3)
399	if !ok {
400		return false, false, errors.New("Invalid extra metadata")
401	}
402
403	// TODO: We lose extraV3.wkbNew and extraV3.rkbNew here. Store
404	// it as part of the mdInfo, so we don't needlessly send it
405	// while flushing.
406
407	err = kbfsmd.CheckWKBID(j.codec, wkbID, extraV3.GetWriterKeyBundle())
408	if err != nil {
409		return false, false, err
410	}
411
412	err = kbfsmd.CheckRKBID(j.codec, rkbID, extraV3.GetReaderKeyBundle())
413	if err != nil {
414		return false, false, err
415	}
416
417	err = kbfscodec.SerializeToFileIfNotExist(
418		j.codec, extraV3.GetWriterKeyBundle(), j.writerKeyBundleV3Path(wkbID))
419	if err != nil {
420		return false, false, err
421	}
422
423	err = kbfscodec.SerializeToFileIfNotExist(
424		j.codec, extraV3.GetReaderKeyBundle(), j.readerKeyBundleV3Path(rkbID))
425	if err != nil {
426		return false, false, err
427	}
428
429	return extraV3.IsWriterKeyBundleNew(), extraV3.IsReaderKeyBundleNew(), nil
430}
431
432// getMDAndExtra verifies the MD data, the writer signature (but not
433// the key), and the extra metadata for the given ID and returns
434// them. It also returns the last-modified timestamp of the
435// file. verifyBranchID should be false only when called from
436// makeMDJournal, i.e. when figuring out what to set j.branchID in the
437// first place.
438//
439// It returns a kbfsmd.MutableRootMetadata so that it can be put in a
440// RootMetadataSigned object.
441func (j mdJournal) getMDAndExtra(ctx context.Context, entry mdIDJournalEntry,
442	verifyBranchID bool) (
443	kbfsmd.MutableRootMetadata, kbfsmd.ExtraMetadata, time.Time, error) {
444	// Read info.
445
446	timestamp, version, err := j.getMDInfo(entry.ID)
447	if err != nil {
448		return nil, nil, time.Time{}, err
449	}
450
451	// Read data.
452
453	p := j.mdDataPath(entry.ID)
454	data, err := ioutil.ReadFile(p)
455	if err != nil {
456		return nil, nil, time.Time{}, err
457	}
458
459	rmd, err := kbfsmd.DecodeRootMetadata(
460		j.codec, j.tlfID, version, j.mdVer, data)
461	if err != nil {
462		return nil, nil, time.Time{}, err
463	}
464
465	// Check integrity.
466
467	mdID, err := kbfsmd.MakeID(j.codec, rmd)
468	if err != nil {
469		return nil, nil, time.Time{}, err
470	}
471
472	if mdID != entry.ID {
473		return nil, nil, time.Time{}, errors.Errorf(
474			"Metadata ID mismatch: expected %s, got %s",
475			entry.ID, mdID)
476	}
477
478	err = rmd.IsLastModifiedBy(j.uid, j.key)
479	if err != nil {
480		return nil, nil, time.Time{}, err
481	}
482
483	extra, err := j.getExtraMetadata(
484		rmd.GetTLFWriterKeyBundleID(), rmd.GetTLFReaderKeyBundleID(),
485		entry.WKBNew, entry.RKBNew)
486	if err != nil {
487		return nil, nil, time.Time{}, err
488	}
489
490	err = rmd.IsValidAndSigned(
491		ctx, j.codec, j.teamMemChecker, extra, j.key,
492		j.osg.OfflineAvailabilityForID(j.tlfID))
493	if err != nil {
494		return nil, nil, time.Time{}, err
495	}
496
497	if verifyBranchID && rmd.BID() != j.branchID &&
498		!(rmd.BID() == kbfsmd.NullBranchID && j.branchID == kbfsmd.PendingLocalSquashBranchID) {
499		return nil, nil, time.Time{}, errors.Errorf(
500			"Branch ID mismatch: expected %s, got %s",
501			j.branchID, rmd.BID())
502	}
503
504	// Local conflict branches will have a different local TLF ID.
505	if j.overrideTlfID != tlf.NullID {
506		rmd.SetTlfID(j.overrideTlfID)
507	}
508
509	return rmd, extra, timestamp, nil
510}
511
512type mdJournalInfo struct {
513	ID kbfsmd.ID
514}
515
516func (j mdJournal) journalInfoPath() string {
517	return filepath.Join(j.j.j.dir, "info")
518}
519
520// getOrCreateJournalID returns the unique ID of the journal, renewed
521// the last time it was cleared.
522func (j *mdJournal) getOrCreateJournalID() (kbfsmd.ID, error) {
523	if j.journalID.IsValid() {
524		return j.journalID, nil
525	}
526
527	// Read it from the file, if the file exists.
528	p := j.journalInfoPath()
529	var info mdJournalInfo
530	err := kbfscodec.DeserializeFromFile(j.codec, p, &info)
531	switch {
532	case err == nil:
533		j.journalID = info.ID
534		return info.ID, nil
535	case ioutil.IsNotExist(errors.Cause(err)):
536		// Continue.
537	default:
538		return kbfsmd.ID{}, err
539	}
540
541	// Read latest entry ID and serialize it into the info file.  We
542	// use the latest entry, rather than the earliest, because when
543	// resolving branches sometimes the earliest entries (local
544	// squashes) are preserved.  It doesn't really matter which ID we
545	// pick as long as it is unique after a branch resolution/clear,
546	// and persisted across restarts.
547	entry, exists, err := j.j.getLatestEntry()
548	if err != nil {
549		return kbfsmd.ID{}, err
550	}
551	if !exists {
552		// No journal ID yet.
553		return kbfsmd.ID{}, nil
554	}
555	info.ID = entry.ID
556	err = kbfscodec.SerializeToFile(j.codec, info, p)
557	if err != nil {
558		return kbfsmd.ID{}, err
559	}
560	j.journalID = info.ID
561	return info.ID, nil
562}
563
564// putMD stores the given metadata under its ID, if it's not already
565// stored. The extra metadata is put separately, since sometimes,
566// (e.g., when converting to a branch) we don't need to put it.
567func (j mdJournal) putMD(rmd kbfsmd.RootMetadata) (mdID kbfsmd.ID, err error) {
568	// TODO: Make crypto and RMD wrap errors.
569
570	err = rmd.IsLastModifiedBy(j.uid, j.key)
571	if err != nil {
572		return kbfsmd.ID{}, err
573	}
574
575	id, err := kbfsmd.MakeID(j.codec, rmd)
576	if err != nil {
577		return kbfsmd.ID{}, err
578	}
579
580	_, err = ioutil.Stat(j.mdDataPath(id))
581	switch {
582	case ioutil.IsNotExist(err):
583		// Continue on.
584	case err != nil:
585		return kbfsmd.ID{}, err
586	default:
587		// Entry exists, so nothing else to do.
588		return id, nil
589	}
590
591	err = kbfscodec.SerializeToFileIfNotExist(
592		j.codec, rmd, j.mdDataPath(id))
593	if err != nil {
594		return kbfsmd.ID{}, err
595	}
596
597	err = j.putMDInfo(id, j.clock.Now(), rmd.Version())
598	if err != nil {
599		return kbfsmd.ID{}, err
600	}
601
602	return id, nil
603}
604
605// removeMD removes the metadata (which must exist) with the given ID.
606func (j *mdJournal) removeMD(id kbfsmd.ID) error {
607	path := j.mdPath(id)
608	err := ioutil.RemoveAll(path)
609	if err != nil {
610		return err
611	}
612
613	// Remove the parent (splayed) directory (which should exist)
614	// if it's empty.
615	err = ioutil.Remove(filepath.Dir(path))
616	if ioutil.IsExist(err) {
617		err = nil
618	}
619	return err
620}
621
622// getEarliestWithExtra returns a kbfsmd.MutableRootMetadata so that it
623// can be put in a RootMetadataSigned object.
624func (j mdJournal) getEarliestWithExtra(
625	ctx context.Context, verifyBranchID bool) (
626	kbfsmd.ID, kbfsmd.MutableRootMetadata, kbfsmd.ExtraMetadata, time.Time, error) {
627	entry, exists, err := j.j.getEarliestEntry()
628	if err != nil {
629		return kbfsmd.ID{}, nil, nil, time.Time{}, err
630	}
631	if !exists {
632		return kbfsmd.ID{}, nil, nil, time.Time{}, nil
633	}
634	earliest, extra, timestamp, err :=
635		j.getMDAndExtra(ctx, entry, verifyBranchID)
636	if err != nil {
637		return kbfsmd.ID{}, nil, nil, time.Time{}, err
638	}
639	return entry.ID, earliest, extra, timestamp, nil
640}
641
642func (j mdJournal) getLatest(ctx context.Context, verifyBranchID bool) (
643	ImmutableBareRootMetadata, error) {
644	entry, exists, err := j.j.getLatestEntry()
645	if err != nil {
646		return ImmutableBareRootMetadata{}, err
647	}
648	if !exists {
649		return ImmutableBareRootMetadata{}, nil
650	}
651	latest, extra, timestamp, err :=
652		j.getMDAndExtra(ctx, entry, verifyBranchID)
653	if err != nil {
654		return ImmutableBareRootMetadata{}, err
655	}
656	return MakeImmutableBareRootMetadata(
657		latest, extra, entry.ID, timestamp), nil
658}
659
660func (j mdJournal) checkGetParams(ctx context.Context) (
661	ImmutableBareRootMetadata, error) {
662	head, err := j.getLatest(ctx, true)
663	if err != nil {
664		return ImmutableBareRootMetadata{}, err
665	}
666
667	if head == (ImmutableBareRootMetadata{}) {
668		return ImmutableBareRootMetadata{}, nil
669	}
670
671	ok, err := isReader(
672		ctx, j.teamMemChecker, j.uid, head.RootMetadata, head.extra)
673	if err != nil {
674		return ImmutableBareRootMetadata{}, err
675	}
676	if !ok {
677		// TODO: Use a non-server error.
678		return ImmutableBareRootMetadata{}, kbfsmd.ServerErrorUnauthorized{}
679	}
680
681	return head, nil
682}
683
684func (j *mdJournal) convertToBranch(
685	ctx context.Context, bid kbfsmd.BranchID, signer kbfscrypto.Signer,
686	codec kbfscodec.Codec, tlfID tlf.ID, mdcache MDCache) (err error) {
687	if j.branchID != kbfsmd.NullBranchID {
688		return errors.Errorf(
689			"convertToBranch called with j.branchID=%s", j.branchID)
690	}
691	if bid == kbfsmd.NullBranchID {
692		return errors.Errorf(
693			"convertToBranch called with null branchID")
694	}
695
696	earliestRevision, err := j.j.readEarliestRevision()
697	if err != nil {
698		return err
699	}
700
701	latestRevision, err := j.j.readLatestRevision()
702	if err != nil {
703		return err
704	}
705
706	j.log.CDebugf(
707		ctx, "rewriting MDs %s to %s", earliestRevision, latestRevision)
708
709	_, allEntries, err := j.j.getEntryRange(
710		earliestRevision, latestRevision)
711	if err != nil {
712		return err
713	}
714
715	j.log.CDebugf(ctx, "New branch ID=%s", bid)
716
717	journalTempDir, err := ioutil.TempDir(j.dir, "md_journal")
718	if err != nil {
719		return err
720	}
721	j.log.CDebugf(ctx, "Using temp dir %s for rewriting", journalTempDir)
722
723	mdsToRemove := make([]kbfsmd.ID, 0, len(allEntries))
724	defer func() {
725		// If we crash here and leave behind the tempdir, it
726		// won't be cleaned up automatically when the journal
727		// is completely drained, but it'll be cleaned up when
728		// the parent journal (i.e., tlfJournal) is completely
729		// drained. As for the entries, they'll be cleaned up
730		// the next time the journal is completely drained.
731
732		j.log.CDebugf(ctx, "Removing temp dir %s and %d old MDs",
733			journalTempDir, len(mdsToRemove))
734		removeErr := ioutil.RemoveAll(journalTempDir)
735		if removeErr != nil {
736			j.log.CWarningf(ctx,
737				"Error when removing temp dir %s: %+v",
738				journalTempDir, removeErr)
739		}
740		// Garbage-collect the unnecessary MD entries.
741		for _, id := range mdsToRemove {
742			removeErr := j.removeMD(id)
743			if removeErr != nil {
744				j.log.CWarningf(ctx, "Error when removing old MD %s: %+v",
745					id, removeErr)
746			}
747		}
748	}()
749
750	tempJournal, err := makeMdIDJournal(j.codec, journalTempDir)
751	if err != nil {
752		return err
753	}
754
755	var prevID kbfsmd.ID
756
757	isPendingLocalSquash := bid == kbfsmd.PendingLocalSquashBranchID
758	for _, entry := range allEntries {
759		brmd, _, ts, err := j.getMDAndExtra(ctx, entry, true)
760		if err != nil {
761			return err
762		}
763
764		if entry.IsLocalSquash && isPendingLocalSquash {
765			// If this is a local squash, don't convert it.  We don't
766			// want to squash anything more into it.
767			j.log.CDebugf(ctx, "Preserving local squash %s", entry.ID)
768			err = tempJournal.append(brmd.RevisionNumber(), entry)
769			if err != nil {
770				return err
771			}
772			continue
773		}
774
775		brmd.SetUnmerged()
776		brmd.SetBranchID(bid)
777
778		// Re-sign the writer metadata internally, since we
779		// changed it.
780		err = brmd.SignWriterMetadataInternally(ctx, j.codec, signer)
781		if err != nil {
782			j.log.CDebugf(ctx, "Early exit %d %+v", brmd.RevisionNumber(), err)
783			return err
784		}
785
786		// Set the prev root for everything after the first MD we
787		// modify, which happens to be indicated by mdsToRemove being
788		// non-empty.
789		if len(mdsToRemove) > 0 {
790			j.log.CDebugf(ctx, "Old prev root of rev=%s is %s",
791				brmd.RevisionNumber(), brmd.GetPrevRoot())
792			j.log.CDebugf(ctx, "Changing prev root of rev=%s to %s",
793				brmd.RevisionNumber(), prevID)
794			brmd.SetPrevRoot(prevID)
795		}
796
797		// TODO: this rewrites the file, and so the modification time
798		// no longer tracks when exactly the original operation is
799		// done, so future ImmutableBareMetadatas for this MD will
800		// have a slightly wrong localTimestamp.  Instead, we might
801		// want to pass in the timestamp and do an explicit
802		// os.Chtimes() on the file after writing it.
803		newID, err := j.putMD(brmd)
804		if err != nil {
805			return err
806		}
807		mdsToRemove = append(mdsToRemove, newID)
808
809		// Preserve unknown fields from the old journal.
810		newEntry := entry
811		newEntry.ID = newID
812		newEntry.IsLocalSquash = false
813		err = tempJournal.append(brmd.RevisionNumber(), newEntry)
814		if err != nil {
815			return err
816		}
817
818		prevID = newID
819
820		// If possible, replace the old RMD in the cache.  If it's not
821		// already in the cache, don't bother adding it, as that will
822		// just evict something incorrectly.  If it's been replaced by
823		// the REAL commit from the master branch due to a race, don't
824		// clobber that real commit. TODO: Don't replace the MD until
825		// we know for sure that the branch conversion succeeds
826		// (however, the Replace doesn't affect correctness since the
827		// original commit will be read from disk instead of the cache
828		// in the event of a conversion failure).
829		oldIrmd, err := mdcache.Get(
830			tlfID, brmd.RevisionNumber(), kbfsmd.NullBranchID)
831		if err == nil && entry.ID == oldIrmd.mdID {
832			newRmd, err := oldIrmd.deepCopy(codec)
833			if err != nil {
834				return err
835			}
836			newRmd.bareMd = brmd
837			// Everything else is the same.
838			err = mdcache.Replace(
839				MakeImmutableRootMetadata(newRmd,
840					oldIrmd.LastModifyingWriterVerifyingKey(),
841					newID, ts, false),
842				kbfsmd.NullBranchID)
843			if err != nil {
844				return err
845			}
846		} else {
847			j.log.CDebugf(ctx,
848				"Not cache-replacing rev=%d: old ID=%s, entry.ID=%s, err=%+v",
849				brmd.RevisionNumber(), oldIrmd.mdID, entry.ID, err)
850		}
851
852		j.log.CDebugf(ctx, "Changing ID for rev=%s from %s to %s",
853			brmd.RevisionNumber(), entry.ID, newID)
854	}
855
856	// TODO: Do the below atomically on the filesystem
857	// level. Specifically, make "md_journal" always be a symlink,
858	// and then perform the swap by atomically changing the
859	// symlink to point to the new journal directory.
860
861	oldJournalTempDir := journalTempDir + ".old"
862	dir, err := j.j.move(oldJournalTempDir)
863	if err != nil {
864		return err
865	}
866
867	j.log.CDebugf(ctx, "Moved old journal from %s to %s",
868		dir, oldJournalTempDir)
869
870	newJournalOldDir, err := tempJournal.move(dir)
871	if err != nil {
872		return err
873	}
874
875	j.log.CDebugf(ctx, "Moved new journal from %s to %s",
876		newJournalOldDir, dir)
877
878	// Make the defer block above remove oldJournalTempDir.
879	journalTempDir = oldJournalTempDir
880
881	mdsToRemove = make([]kbfsmd.ID, 0, len(allEntries))
882	for _, entry := range allEntries {
883		if entry.IsLocalSquash && isPendingLocalSquash {
884			continue
885		}
886		mdsToRemove = append(mdsToRemove, entry.ID)
887	}
888
889	j.j = tempJournal
890	j.branchID = bid
891	j.journalID = kbfsmd.ID{}
892
893	return nil
894}
895
896// getNextEntryToFlush returns the info for the next journal entry to
897// flush, if it exists, and its revision is less than end. If there is
898// no next journal entry to flush, the returned MdID will be zero, and
899// the returned *RootMetadataSigned will be nil.
900func (j mdJournal) getNextEntryToFlush(
901	ctx context.Context, end kbfsmd.Revision, signer kbfscrypto.Signer) (
902	kbfsmd.ID, *RootMetadataSigned, kbfsmd.ExtraMetadata, error) {
903	mdID, rmd, extra, timestamp, err := j.getEarliestWithExtra(ctx, true)
904	if err != nil {
905		return kbfsmd.ID{}, nil, nil, err
906	}
907	if rmd == nil || rmd.RevisionNumber() >= end {
908		return kbfsmd.ID{}, nil, nil, nil
909	}
910
911	rmds, err := SignBareRootMetadata(
912		ctx, j.codec, signer, signer, rmd, timestamp)
913	if err != nil {
914		return kbfsmd.ID{}, nil, nil, err
915	}
916
917	return mdID, rmds, extra, nil
918}
919
920func (j *mdJournal) removeFlushedEntry(
921	ctx context.Context, mdID kbfsmd.ID, rmds *RootMetadataSigned) (
922	clearedMDJournal bool, err error) {
923	rmdID, rmd, _, _, err := j.getEarliestWithExtra(ctx, true)
924	if err != nil {
925		return false, err
926	}
927	if rmd == nil {
928		return false, errors.New("mdJournal unexpectedly empty")
929	}
930
931	if mdID != rmdID {
932		return false, errors.Errorf("Expected mdID %s, got %s", mdID, rmdID)
933	}
934
935	eq, err := kbfscodec.Equal(j.codec, rmd, rmds.MD)
936	if err != nil {
937		return false, err
938	}
939	if !eq {
940		return false, errors.New(
941			"Given RootMetadataSigned doesn't match earliest")
942	}
943
944	empty, err := j.j.removeEarliest()
945	if err != nil {
946		return false, err
947	}
948
949	// Since the journal is now empty, set lastMdID and nuke all
950	// MD-related directories.
951	if empty {
952		j.log.CDebugf(ctx,
953			"MD journal is now empty; saving last MdID=%s", mdID)
954		j.lastMdID = mdID
955
956		// The disk journal has already been cleared, so we
957		// can nuke the directories without having to worry
958		// about putting the journal in a weird state if we
959		// crash in the middle. The various directories will
960		// be recreated as needed.
961		for _, dir := range j.mdJournalDirs() {
962			j.log.CDebugf(ctx, "Removing all files in %s", dir)
963			err := ioutil.RemoveAll(dir)
964			if err != nil {
965				return false, err
966			}
967		}
968
969		return true, nil
970	}
971
972	// Garbage-collect the old entry. If we crash here and
973	// leave behind an entry, it'll be cleaned up the next
974	// time the journal is completely drained.
975	err = j.removeMD(mdID)
976	if err != nil {
977		return false, err
978	}
979
980	return false, nil
981}
982
983func getMdID(ctx context.Context, mdserver MDServer, codec kbfscodec.Codec,
984	tlfID tlf.ID, bid kbfsmd.BranchID, mStatus kbfsmd.MergeStatus,
985	revision kbfsmd.Revision, lockBeforeGet *keybase1.LockID) (kbfsmd.ID, error) {
986	rmdses, err := mdserver.GetRange(
987		ctx, tlfID, bid, mStatus, revision, revision, lockBeforeGet)
988	switch {
989	case err != nil:
990		return kbfsmd.ID{}, err
991	case len(rmdses) == 0:
992		return kbfsmd.ID{}, nil
993	case len(rmdses) > 1:
994		return kbfsmd.ID{}, errors.Errorf(
995			"Got more than one object when trying to get rev=%d for branch %s of TLF %s",
996			revision, bid, tlfID)
997	}
998
999	return kbfsmd.MakeID(codec, rmdses[0].MD)
1000}
1001
1002// clearHelper removes all the journal entries starting from
1003// earliestBranchRevision and deletes the corresponding MD
1004// updates. All MDs from earliestBranchRevision onwards must have
1005// branch equal to the given one, which must not be kbfsmd.NullBranchID. This
1006// means that, if bid != kbfsmd.PendingLocalSquashBranchID,
1007// earliestBranchRevision must equal the earliest revision, and if bid
1008// == kbfsmd.PendingLocalSquashBranchID, earliestBranchRevision must equal
1009// one past the last local squash revision. If the branch is a pending
1010// local squash, it preserves the MD updates corresponding to the
1011// prefix of existing local squashes, so they can be re-used in the
1012// newly-resolved journal.
1013func (j *mdJournal) clearHelper(ctx context.Context, bid kbfsmd.BranchID,
1014	earliestBranchRevision kbfsmd.Revision) (err error) {
1015	j.log.CDebugf(ctx, "Clearing journal for branch %s", bid)
1016	defer func() {
1017		if err != nil {
1018			j.deferLog.CDebugf(ctx,
1019				"Clearing journal for branch %s failed with %+v",
1020				bid, err)
1021		}
1022	}()
1023
1024	if bid == kbfsmd.NullBranchID {
1025		return errors.New("Cannot clear master branch")
1026	}
1027
1028	if j.branchID != bid {
1029		// Nothing to do.
1030		j.log.CDebugf(ctx, "Ignoring clear for branch %s while on branch %s",
1031			bid, j.branchID)
1032		return nil
1033	}
1034
1035	head, err := j.getHead(ctx, bid)
1036	if err != nil {
1037		return err
1038	}
1039
1040	if head == (ImmutableBareRootMetadata{}) {
1041		// The journal has been flushed but not cleared yet.
1042		j.branchID = kbfsmd.NullBranchID
1043		j.journalID = kbfsmd.ID{}
1044		return nil
1045	}
1046
1047	if head.BID() != j.branchID {
1048		return errors.Errorf("Head branch ID %s doesn't match journal "+
1049			"branch ID %s while clearing", head.BID(), j.branchID)
1050	}
1051
1052	latestRevision, err := j.j.readLatestRevision()
1053	if err != nil {
1054		return err
1055	}
1056
1057	_, allEntries, err := j.j.getEntryRange(
1058		earliestBranchRevision, latestRevision)
1059	if err != nil {
1060		return err
1061	}
1062
1063	err = j.j.clearFrom(earliestBranchRevision)
1064	if err != nil {
1065		return err
1066	}
1067
1068	j.branchID = kbfsmd.NullBranchID
1069	j.journalID = kbfsmd.ID{}
1070
1071	// No need to set lastMdID in this case.
1072
1073	// Garbage-collect the old branch entries.  TODO: we'll eventually
1074	// need a sweeper to clean up entries left behind if we crash
1075	// here.
1076	for _, entry := range allEntries {
1077		err := j.removeMD(entry.ID)
1078		if err != nil {
1079			return err
1080		}
1081	}
1082	return nil
1083}
1084
1085// All functions below are public functions.
1086
1087func (j mdJournal) readEarliestRevision() (kbfsmd.Revision, error) {
1088	return j.j.readEarliestRevision()
1089}
1090
1091func (j mdJournal) readLatestRevision() (kbfsmd.Revision, error) {
1092	return j.j.readLatestRevision()
1093}
1094
1095func (j mdJournal) length() uint64 {
1096	return j.j.length()
1097}
1098
1099func (j mdJournal) atLeastNNonLocalSquashes(
1100	numNonLocalSquashes uint64) (bool, error) {
1101	size := j.length()
1102	if size < numNonLocalSquashes {
1103		return false, nil
1104	}
1105
1106	latestRev, err := j.readLatestRevision()
1107	if err != nil {
1108		return false, err
1109	}
1110
1111	// Since the IsLocalSquash entries are guaranteed to be a prefix
1112	// of the journal, we can just look up an entry that's back
1113	// `numNonLocalSquashes` entries ago, and see if it's a local
1114	// squash or not.
1115	entry, err := j.j.readJournalEntry(
1116		latestRev - kbfsmd.Revision(numNonLocalSquashes) + 1)
1117	if err != nil {
1118		return false, err
1119	}
1120
1121	return !entry.IsLocalSquash, nil
1122}
1123
1124func (j mdJournal) end() (kbfsmd.Revision, error) {
1125	return j.j.end()
1126}
1127
1128func (j mdJournal) getBranchID() kbfsmd.BranchID {
1129	return j.branchID
1130}
1131
1132func (j mdJournal) getHead(ctx context.Context, bid kbfsmd.BranchID) (
1133	ImmutableBareRootMetadata, error) {
1134	head, err := j.checkGetParams(ctx)
1135	if err != nil {
1136		return ImmutableBareRootMetadata{}, err
1137	}
1138	if head == (ImmutableBareRootMetadata{}) {
1139		return ImmutableBareRootMetadata{}, nil
1140	}
1141
1142	getLocalSquashHead := bid == kbfsmd.NullBranchID &&
1143		j.branchID == kbfsmd.PendingLocalSquashBranchID
1144	if !getLocalSquashHead {
1145		if head.BID() != bid {
1146			return ImmutableBareRootMetadata{}, nil
1147		}
1148		return head, nil
1149	}
1150
1151	// Look backwards in the journal for the first entry with
1152	// IsLocalSquash set to true.
1153	earliestRev, err := j.readEarliestRevision()
1154	if err != nil {
1155		return ImmutableBareRootMetadata{}, err
1156	}
1157
1158	latestRev, err := j.readLatestRevision()
1159	if err != nil {
1160		return ImmutableBareRootMetadata{}, err
1161	}
1162
1163	for rev := latestRev; rev >= earliestRev; rev-- {
1164		entry, err := j.j.readJournalEntry(rev)
1165		if err != nil {
1166			return ImmutableBareRootMetadata{}, err
1167		}
1168		if entry.IsLocalSquash {
1169			latest, extra, timestamp, err :=
1170				j.getMDAndExtra(ctx, entry, false)
1171			if err != nil {
1172				return ImmutableBareRootMetadata{}, err
1173			}
1174			return MakeImmutableBareRootMetadata(
1175				latest, extra, entry.ID, timestamp), nil
1176		}
1177	}
1178	return ImmutableBareRootMetadata{}, nil
1179}
1180
1181func (j mdJournal) getRange(
1182	ctx context.Context, bid kbfsmd.BranchID, start, stop kbfsmd.Revision) (
1183	[]ImmutableBareRootMetadata, error) {
1184	head, err := j.checkGetParams(ctx)
1185	if err != nil {
1186		return nil, err
1187	} else if head == (ImmutableBareRootMetadata{}) {
1188		return nil, nil
1189	}
1190
1191	// If we are on a pending local squash branch, the caller can ask
1192	// for "merged" entries that make up a prefix of the journal.
1193	getLocalSquashPrefix := bid == kbfsmd.NullBranchID &&
1194		j.branchID == kbfsmd.PendingLocalSquashBranchID
1195	if head.BID() != bid && !getLocalSquashPrefix {
1196		return nil, nil
1197	}
1198
1199	realStart, entries, err := j.j.getEntryRange(start, stop)
1200	if err != nil {
1201		return nil, err
1202	}
1203	var ibrmds []ImmutableBareRootMetadata
1204	for i, entry := range entries {
1205		if getLocalSquashPrefix && !entry.IsLocalSquash {
1206			// We only need the prefix up to the first non-local-squash.
1207			break
1208		} else if entry.IsLocalSquash && bid == kbfsmd.PendingLocalSquashBranchID {
1209			// Ignore the local squash prefix of this journal.
1210			continue
1211		}
1212
1213		expectedRevision := realStart + kbfsmd.Revision(i)
1214		brmd, extra, ts, err := j.getMDAndExtra(ctx, entry, true)
1215		if err != nil {
1216			return nil, err
1217		}
1218
1219		if expectedRevision != brmd.RevisionNumber() {
1220			panic(errors.Errorf("expected revision %v, got %v",
1221				expectedRevision, brmd.RevisionNumber()))
1222		}
1223		ibrmd := MakeImmutableBareRootMetadata(
1224			brmd, extra, entry.ID, ts)
1225		ibrmds = append(ibrmds, ibrmd)
1226	}
1227
1228	return ibrmds, nil
1229}
1230
1231// MDJournalConflictError is an error that is returned when a put
1232// detects a rewritten journal.
1233type MDJournalConflictError struct{}
1234
1235func (e MDJournalConflictError) Error() string {
1236	return "MD journal conflict error"
1237}
1238
1239// put verifies and stores the given RootMetadata in the journal,
1240// modifying it as needed. In particular, there are four cases:
1241//
1242// Merged
1243// ------
1244// rmd is merged. If the journal is empty, then rmd becomes the
1245// initial entry. Otherwise, if the journal has been converted to a
1246// branch, then an MDJournalConflictError error is returned, and the
1247// caller is expected to set the unmerged bit and retry (see case
1248// Unmerged-1). Otherwise, either rmd must be the successor to the
1249// journal's head, in which case it is appended, or it must have the
1250// same revision number as the journal's head, in which case it
1251// replaces the journal's head. (This is necessary since if a journal
1252// put is cancelled and an error is returned, it still happens, and so
1253// we want the retried put (if any) to not conflict with it.)
1254//
1255// Unmerged-1
1256// ----------
1257// rmd is unmerged and has a null branch ID. This happens when case
1258// Merged returns with MDJournalConflictError. In this case, the rmd's
1259// branch ID is set to the journal's branch ID and its prevRoot is set
1260// to the last known journal root. It doesn't matter if the journal is
1261// completely drained, since the branch ID and last known root is
1262// remembered in memory. However, since this cache isn't persisted to
1263// disk, we need case Unmerged-3. Similarly to case Merged, this case
1264// then also does append-or-replace.
1265//
1266// Unmerged-2
1267// ----------
1268// rmd is unmerged and has a non-null branch ID, and the journal was
1269// non-empty at some time during this process's lifetime. Similarly to
1270// case Merged, if the journal is empty, then rmd becomes the initial
1271// entry, and otherwise, this case does append-or-replace.
1272//
1273// Unmerged-3
1274// ----------
1275// rmd is unmerged and has a non-null branch ID, and the journal has
1276// always been empty during this process's lifetime. The branch ID is
1277// assumed to be correct, i.e. retrieved from the remote MDServer, and
1278// rmd becomes the initial entry.
1279func (j *mdJournal) put(
1280	ctx context.Context, signer kbfscrypto.Signer,
1281	ekg encryptionKeyGetter, bsplit data.BlockSplitter, rmd *RootMetadata,
1282	isLocalSquash bool) (
1283	mdID, journalID kbfsmd.ID, err error) {
1284	j.log.CDebugf(ctx, "Putting MD for TLF=%s with rev=%s bid=%s",
1285		rmd.TlfID(), rmd.Revision(), rmd.BID())
1286	defer func() {
1287		if err != nil {
1288			j.deferLog.CDebugf(ctx,
1289				"Put MD for TLF=%s with rev=%s bid=%s failed with %+v",
1290				rmd.TlfID(), rmd.Revision(), rmd.BID(), err)
1291		}
1292	}()
1293
1294	head, err := j.getLatest(ctx, true)
1295	if err != nil {
1296		return kbfsmd.ID{}, kbfsmd.ID{}, err
1297	}
1298
1299	mStatus := rmd.MergedStatus()
1300
1301	// Make modifications for the Unmerged cases.
1302	if mStatus == kbfsmd.Unmerged {
1303		var lastMdID kbfsmd.ID
1304		if head == (ImmutableBareRootMetadata{}) {
1305			lastMdID = j.lastMdID
1306		} else {
1307			lastMdID = head.mdID
1308		}
1309
1310		if rmd.BID() == kbfsmd.NullBranchID && j.branchID == kbfsmd.NullBranchID {
1311			return kbfsmd.ID{}, kbfsmd.ID{}, errors.New(
1312				"Unmerged put with rmd.BID() == j.branchID == kbfsmd.NullBranchID")
1313		}
1314
1315		switch {
1316		case head == (ImmutableBareRootMetadata{}) &&
1317			j.branchID == kbfsmd.NullBranchID:
1318			// Case Unmerged-3.
1319			j.branchID = rmd.BID()
1320			// Revert branch ID if we encounter an error.
1321			defer func() {
1322				if err != nil {
1323					j.branchID = kbfsmd.NullBranchID
1324				}
1325			}()
1326		case rmd.BID() == kbfsmd.NullBranchID:
1327			// Case Unmerged-1.
1328			j.log.CDebugf(
1329				ctx, "Changing branch ID to %s and prev root to %s for MD for TLF=%s with rev=%s",
1330				j.branchID, lastMdID, rmd.TlfID(), rmd.Revision())
1331			rmd.SetBranchID(j.branchID)
1332			rmd.SetPrevRoot(lastMdID)
1333		default: // nolint
1334			// Using de Morgan's laws, this branch is
1335			// taken when both rmd.BID() is non-null, and
1336			// either head is non-empty or j.branchID is
1337			// non-empty. So this is most of case
1338			// Unmerged-2, and there's nothing to do.
1339			//
1340			// The remaining part of case Unmerged-2,
1341			// where rmd.BID() is non-null, head is empty,
1342			// and j.branchID is empty, is an error case,
1343			// handled below.
1344		}
1345	}
1346
1347	// The below is code common to all the cases.
1348
1349	if (mStatus == kbfsmd.Merged) != (rmd.BID() == kbfsmd.NullBranchID) {
1350		return kbfsmd.ID{}, kbfsmd.ID{}, errors.Errorf(
1351			"mStatus=%s doesn't match bid=%s", mStatus, rmd.BID())
1352	}
1353
1354	// If we're trying to push a merged MD onto a branch, return a
1355	// conflict error so the caller can retry with an unmerged MD.
1356	if mStatus == kbfsmd.Merged && j.branchID != kbfsmd.NullBranchID {
1357		return kbfsmd.ID{}, kbfsmd.ID{}, MDJournalConflictError{}
1358	}
1359
1360	if rmd.BID() != j.branchID {
1361		return kbfsmd.ID{}, kbfsmd.ID{}, errors.Errorf(
1362			"Branch ID mismatch: expected %s, got %s",
1363			j.branchID, rmd.BID())
1364	}
1365
1366	if isLocalSquash && rmd.BID() != kbfsmd.NullBranchID {
1367		return kbfsmd.ID{}, kbfsmd.ID{},
1368			errors.Errorf("A local squash must have a null branch ID,"+
1369				" but this one has bid=%s", rmd.BID())
1370	}
1371
1372	// Check permissions and consistency with head, if it exists.
1373	if head != (ImmutableBareRootMetadata{}) {
1374		ok, err := isWriterOrValidRekey(
1375			ctx, j.teamMemChecker, j.codec, j.uid, j.key, head.RootMetadata,
1376			rmd.bareMd, head.extra, rmd.extra)
1377		if err != nil {
1378			return kbfsmd.ID{}, kbfsmd.ID{}, err
1379		}
1380		if !ok {
1381			// TODO: Use a non-server error.
1382			return kbfsmd.ID{}, kbfsmd.ID{}, kbfsmd.ServerErrorUnauthorized{}
1383		}
1384
1385		// Consistency checks
1386		if rmd.Revision() != head.RevisionNumber() {
1387			err = head.CheckValidSuccessorForServer(
1388				head.mdID, rmd.bareMd)
1389			if err != nil {
1390				return kbfsmd.ID{}, kbfsmd.ID{}, err
1391			}
1392		}
1393
1394		// Local squashes should only be preceded by another local
1395		// squash in the journal.
1396		if isLocalSquash {
1397			entry, exists, err := j.j.getLatestEntry()
1398			if err != nil {
1399				return kbfsmd.ID{}, kbfsmd.ID{}, err
1400			}
1401			if exists && !entry.IsLocalSquash {
1402				return kbfsmd.ID{}, kbfsmd.ID{},
1403					errors.Errorf("Local squash is not preceded "+
1404						"by a local squash (head=%s)", entry.ID)
1405			}
1406		}
1407	}
1408
1409	// Ensure that the block changes are properly unembedded.
1410	if rmd.data.Changes.Info.BlockPointer == data.ZeroPtr &&
1411		!bsplit.ShouldEmbedData(rmd.data.Changes.SizeEstimate()) {
1412		return kbfsmd.ID{}, kbfsmd.ID{},
1413			errors.New("MD has embedded block changes, but shouldn't")
1414	}
1415
1416	err = encryptMDPrivateData(
1417		ctx, j.codec, j.crypto, signer, ekg, j.uid, rmd)
1418	if err != nil {
1419		return kbfsmd.ID{}, kbfsmd.ID{}, err
1420	}
1421
1422	err = rmd.bareMd.IsValidAndSigned(
1423		ctx, j.codec, j.teamMemChecker, rmd.extra, j.key,
1424		j.osg.OfflineAvailabilityForID(j.tlfID))
1425	if err != nil {
1426		return kbfsmd.ID{}, kbfsmd.ID{}, err
1427	}
1428
1429	id, err := j.putMD(rmd.bareMd)
1430	if err != nil {
1431		return kbfsmd.ID{}, kbfsmd.ID{}, err
1432	}
1433
1434	wkbNew, rkbNew, err := j.putExtraMetadata(rmd.bareMd, rmd.extra)
1435	if err != nil {
1436		return kbfsmd.ID{}, kbfsmd.ID{}, err
1437	}
1438
1439	newEntry := mdIDJournalEntry{
1440		ID:            id,
1441		IsLocalSquash: isLocalSquash,
1442		WKBNew:        wkbNew,
1443		RKBNew:        rkbNew,
1444	}
1445	if head != (ImmutableBareRootMetadata{}) &&
1446		rmd.Revision() == head.RevisionNumber() {
1447
1448		j.log.CDebugf(
1449			ctx, "Replacing head MD for TLF=%s with rev=%s bid=%s",
1450			rmd.TlfID(), rmd.Revision(), rmd.BID())
1451		// Don't try and preserve unknown fields from the old
1452		// head here -- the new head is in general a different
1453		// MD, so the unknown fields from the old head won't
1454		// make sense.
1455		err = j.j.replaceHead(newEntry)
1456		if err != nil {
1457			return kbfsmd.ID{}, kbfsmd.ID{}, err
1458		}
1459	} else {
1460		err = j.j.append(rmd.Revision(), newEntry)
1461		if err != nil {
1462			return kbfsmd.ID{}, kbfsmd.ID{}, err
1463		}
1464	}
1465
1466	// Since the journal is now non-empty, clear lastMdID.
1467	j.lastMdID = kbfsmd.ID{}
1468
1469	journalID, err = j.getOrCreateJournalID()
1470	if err != nil {
1471		return kbfsmd.ID{}, kbfsmd.ID{}, err
1472	}
1473
1474	return id, journalID, nil
1475}
1476
1477// clear removes all the journal entries, and deletes the
1478// corresponding MD updates.  If the branch is a pending local squash,
1479// it preserves the MD updates corresponding to the prefix of existing
1480// local squashes, so they can be re-used in the newly-resolved
1481// journal.
1482func (j *mdJournal) clear(ctx context.Context, bid kbfsmd.BranchID) error {
1483	earliestBranchRevision, err := j.j.readEarliestRevision()
1484	if err != nil {
1485		return err
1486	}
1487
1488	if earliestBranchRevision != kbfsmd.RevisionUninitialized &&
1489		bid == kbfsmd.PendingLocalSquashBranchID {
1490		latestRevision, err := j.j.readLatestRevision()
1491		if err != nil {
1492			return err
1493		}
1494
1495		for ; earliestBranchRevision <= latestRevision; earliestBranchRevision++ {
1496			entry, err := j.j.readJournalEntry(earliestBranchRevision)
1497			if err != nil {
1498				return err
1499			}
1500			if !entry.IsLocalSquash {
1501				break
1502			}
1503		}
1504	}
1505
1506	return j.clearHelper(ctx, bid, earliestBranchRevision)
1507}
1508
1509func (j *mdJournal) resolveAndClear(
1510	ctx context.Context, signer kbfscrypto.Signer, ekg encryptionKeyGetter,
1511	bsplit data.BlockSplitter, mdcache MDCache, bid kbfsmd.BranchID,
1512	rmd *RootMetadata) (mdID, journalID kbfsmd.ID, err error) {
1513	j.log.CDebugf(ctx, "Resolve and clear, branch %s, resolve rev %d",
1514		bid, rmd.Revision())
1515	defer func() {
1516		if err != nil {
1517			j.deferLog.CDebugf(ctx,
1518				"Resolving journal for branch %s failed with %+v",
1519				bid, err)
1520		}
1521	}()
1522
1523	// The resolution must not have a branch ID.
1524	if rmd.BID() != kbfsmd.NullBranchID {
1525		return kbfsmd.ID{}, kbfsmd.ID{},
1526			errors.Errorf("Resolution MD has branch ID: %s", rmd.BID())
1527	}
1528
1529	// The branch ID must match our current state.
1530	if bid == kbfsmd.NullBranchID {
1531		return kbfsmd.ID{}, kbfsmd.ID{},
1532			errors.New("Cannot resolve master branch")
1533	}
1534	if j.branchID != bid {
1535		return kbfsmd.ID{}, kbfsmd.ID{},
1536			errors.Errorf("Resolve and clear for branch %s "+
1537				"while on branch %s", bid, j.branchID)
1538	}
1539
1540	earliestBranchRevision, err := j.j.readEarliestRevision()
1541	if err != nil {
1542		return kbfsmd.ID{}, kbfsmd.ID{}, err
1543	}
1544
1545	latestRevision, err := j.j.readLatestRevision()
1546	if err != nil {
1547		return kbfsmd.ID{}, kbfsmd.ID{}, err
1548	}
1549
1550	// First make a new journal to hold the block.
1551
1552	// Give this new journal a new ID journal.
1553	idJournalTempDir, err := ioutil.TempDir(j.dir, "md_journal")
1554	if err != nil {
1555		return kbfsmd.ID{}, kbfsmd.ID{}, err
1556	}
1557
1558	// TODO: If we crash without removing the temp dir, it should
1559	// be cleaned up whenever the entire journal goes empty.
1560
1561	j.log.CDebugf(ctx, "Using temp dir %s for new IDs", idJournalTempDir)
1562	otherIDJournal, err := makeMdIDJournal(j.codec, idJournalTempDir)
1563	if err != nil {
1564		return kbfsmd.ID{}, kbfsmd.ID{}, err
1565	}
1566	defer func() {
1567		j.log.CDebugf(ctx, "Removing temp dir %s", idJournalTempDir)
1568		removeErr := ioutil.RemoveAll(idJournalTempDir)
1569		if removeErr != nil {
1570			j.log.CWarningf(ctx,
1571				"Error when removing temp dir %s: %+v",
1572				idJournalTempDir, removeErr)
1573		}
1574	}()
1575
1576	otherJournal, err := makeMDJournalWithIDJournal(
1577		ctx, j.uid, j.key, j.codec, j.crypto, j.clock, j.teamMemChecker, j.osg,
1578		j.tlfID, j.mdVer, j.dir, otherIDJournal, j.log, j.overrideTlfID)
1579	if err != nil {
1580		return kbfsmd.ID{}, kbfsmd.ID{}, err
1581	}
1582
1583	// Put the local squashes back into the new journal, since they
1584	// weren't part of the resolve.
1585	if bid == kbfsmd.PendingLocalSquashBranchID {
1586		for ; earliestBranchRevision <= latestRevision; earliestBranchRevision++ {
1587			entry, err := j.j.readJournalEntry(earliestBranchRevision)
1588			if err != nil {
1589				return kbfsmd.ID{}, kbfsmd.ID{}, err
1590			}
1591			if !entry.IsLocalSquash {
1592				break
1593			}
1594			j.log.CDebugf(ctx, "Preserving entry %s", entry.ID)
1595			err = otherIDJournal.append(earliestBranchRevision, entry)
1596			if err != nil {
1597				return kbfsmd.ID{}, kbfsmd.ID{}, err
1598			}
1599		}
1600	}
1601
1602	mdID, journalID, err = otherJournal.put(
1603		ctx, signer, ekg, bsplit, rmd, true)
1604	if err != nil {
1605		return kbfsmd.ID{}, kbfsmd.ID{}, err
1606	}
1607
1608	// Transform this journal into the new one.
1609
1610	// TODO: Do the below atomically on the filesystem
1611	// level. Specifically, make "md_journal" always be a symlink,
1612	// and then perform the swap by atomically changing the
1613	// symlink to point to the new journal directory.
1614
1615	oldIDJournalTempDir := idJournalTempDir + ".old"
1616	dir, err := j.j.move(oldIDJournalTempDir)
1617	if err != nil {
1618		return kbfsmd.ID{}, kbfsmd.ID{}, err
1619	}
1620
1621	j.log.CDebugf(ctx, "Moved old journal from %s to %s",
1622		dir, oldIDJournalTempDir)
1623
1624	otherIDJournalOldDir, err := otherJournal.j.move(dir)
1625	if err != nil {
1626		return kbfsmd.ID{}, kbfsmd.ID{}, err
1627	}
1628
1629	// Set new journal to one with the new revision.
1630	j.log.CDebugf(ctx, "Moved new journal from %s to %s",
1631		otherIDJournalOldDir, dir)
1632
1633	// Transform the other journal into the old journal and clear
1634	// it out.
1635	*j, *otherJournal = *otherJournal, *j
1636	err = otherJournal.clearHelper(ctx, bid, earliestBranchRevision)
1637	if err != nil {
1638		return kbfsmd.ID{}, kbfsmd.ID{}, err
1639	}
1640
1641	// Make the defer above remove the old temp dir.
1642	idJournalTempDir = oldIDJournalTempDir
1643
1644	// Delete all of the branch MDs from the md cache.
1645	for rev := earliestBranchRevision; rev <= latestRevision; rev++ {
1646		mdcache.Delete(j.tlfID, rev, bid)
1647	}
1648
1649	return mdID, journalID, nil
1650}
1651
1652// markLatestAsLocalSquash marks the head revision as a local squash,
1653// without the need to go through resolveAndClear.  It's assumed that
1654// the caller already guaranteed that there is no more than 1
1655// non-local-squash at the end of the journal.
1656func (j *mdJournal) markLatestAsLocalSquash(ctx context.Context) error {
1657	if j.branchID != kbfsmd.NullBranchID {
1658		return errors.Errorf("Can't mark latest as local squash when on a "+
1659			"branch (bid=%s)", j.branchID)
1660	}
1661
1662	entry, exists, err := j.j.getLatestEntry()
1663	if err != nil {
1664		return err
1665	}
1666	if !exists || entry.IsLocalSquash {
1667		return nil
1668	}
1669
1670	entry.IsLocalSquash = true
1671	return j.j.replaceHead(entry)
1672}
1673