github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/md_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  	"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.
    38  type 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.
    47  func 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.
    58  func (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.
   125  type 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  
   171  func 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  
   233  func mdJournalPath(dir string) string {
   234  	return filepath.Join(dir, "md_journal")
   235  }
   236  
   237  func 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  
   255  func (j mdJournal) mdsPath() string {
   256  	return filepath.Join(j.dir, "mds")
   257  }
   258  
   259  func (j mdJournal) writerKeyBundlesV3Path() string {
   260  	return filepath.Join(j.dir, "wkbv3")
   261  }
   262  
   263  func (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  
   276  func (j mdJournal) writerKeyBundleV3Path(id kbfsmd.TLFWriterKeyBundleID) string {
   277  	idStr := id.String()
   278  	return filepath.Join(j.writerKeyBundlesV3Path(), idStr[:34])
   279  }
   280  
   281  func (j mdJournal) readerKeyBundleV3Path(id kbfsmd.TLFReaderKeyBundleID) string {
   282  	idStr := id.String()
   283  	return filepath.Join(j.readerKeyBundlesV3Path(), idStr[:34])
   284  }
   285  
   286  func (j mdJournal) mdJournalDirs() []string {
   287  	return []string{
   288  		mdJournalPath(j.dir), j.mdsPath(),
   289  		j.writerKeyBundlesV3Path(), j.readerKeyBundlesV3Path(),
   290  	}
   291  }
   292  
   293  func (j mdJournal) mdPath(id kbfsmd.ID) string {
   294  	idStr := id.String()
   295  	return filepath.Join(j.mdsPath(), idStr[:4], idStr[4:34])
   296  }
   297  
   298  func (j mdJournal) mdDataPath(id kbfsmd.ID) string {
   299  	return filepath.Join(j.mdPath(id), "data")
   300  }
   301  
   302  func (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.
   310  type mdInfo struct {
   311  	Timestamp time.Time
   312  	Version   kbfsmd.MetadataVer
   313  }
   314  
   315  func (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.
   327  func (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.
   335  func (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  
   375  func (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.
   441  func (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  
   512  type mdJournalInfo struct {
   513  	ID kbfsmd.ID
   514  }
   515  
   516  func (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.
   522  func (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.
   567  func (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.
   606  func (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.
   624  func (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  
   642  func (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  
   660  func (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  
   684  func (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.
   900  func (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  
   920  func (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  
   983  func 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.
  1013  func (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  
  1087  func (j mdJournal) readEarliestRevision() (kbfsmd.Revision, error) {
  1088  	return j.j.readEarliestRevision()
  1089  }
  1090  
  1091  func (j mdJournal) readLatestRevision() (kbfsmd.Revision, error) {
  1092  	return j.j.readLatestRevision()
  1093  }
  1094  
  1095  func (j mdJournal) length() uint64 {
  1096  	return j.j.length()
  1097  }
  1098  
  1099  func (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  
  1124  func (j mdJournal) end() (kbfsmd.Revision, error) {
  1125  	return j.j.end()
  1126  }
  1127  
  1128  func (j mdJournal) getBranchID() kbfsmd.BranchID {
  1129  	return j.branchID
  1130  }
  1131  
  1132  func (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  
  1181  func (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.
  1233  type MDJournalConflictError struct{}
  1234  
  1235  func (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.
  1279  func (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.
  1482  func (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  
  1509  func (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.
  1656  func (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  }