github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/block_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  	"fmt"
     9  	"math"
    10  	"path/filepath"
    11  	"reflect"
    12  
    13  	"github.com/keybase/client/go/kbfs/data"
    14  	"github.com/keybase/client/go/kbfs/ioutil"
    15  	"github.com/keybase/client/go/kbfs/kbfsblock"
    16  	"github.com/keybase/client/go/kbfs/kbfscodec"
    17  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    18  	"github.com/keybase/client/go/kbfs/kbfsmd"
    19  	"github.com/keybase/client/go/kbfs/tlf"
    20  	"github.com/keybase/client/go/libkb"
    21  	"github.com/keybase/client/go/logger"
    22  	"github.com/keybase/client/go/protocol/keybase1"
    23  	"github.com/keybase/go-codec/codec"
    24  	"github.com/pkg/errors"
    25  	"golang.org/x/net/context"
    26  )
    27  
    28  // blockJournal stores a single ordered list of block operations for a
    29  // single TLF, along with the associated block data, in flat files in
    30  // a directory on disk.
    31  //
    32  // The directory layout looks like:
    33  //
    34  // dir/block_aggregate_info
    35  // dir/block_journal/EARLIEST
    36  // dir/block_journal/LATEST
    37  // dir/block_journal/0...000
    38  // dir/block_journal/0...001
    39  // dir/block_journal/0...fff
    40  // dir/blocks/...
    41  // dir/gc_block_journal/EARLIEST
    42  // dir/gc_block_journal/LATEST
    43  // dir/gc_block_journal/...
    44  //
    45  // block_aggregate_info holds aggregate info about the block journal;
    46  // currently it just holds the count of stored and unflushed bytes.
    47  //
    48  // Each entry in the journal in dir/block_journal contains the
    49  // mutating operation and arguments for a single operation, except for
    50  // block data. (See diskJournal comments for more details about the
    51  // journal.)
    52  //
    53  // The block data is stored separately in dir/blocks. See
    54  // blockDiskStore comments for more details.
    55  //
    56  // The maximum number of characters added to the root dir by a block
    57  // journal is 51:
    58  //
    59  //	/blocks/(max 44 characters)
    60  //
    61  // blockJournal is not goroutine-safe, so any code that uses it must
    62  // guarantee that only one goroutine at a time calls its functions.
    63  type blockJournal struct {
    64  	codec kbfscodec.Codec
    65  	dir   string
    66  
    67  	log      traceLogger
    68  	deferLog traceLogger
    69  	vlog     *libkb.VDebugLog
    70  
    71  	// j is the main journal.
    72  	j *diskJournal
    73  
    74  	// saveUntilMDFlush, when non-nil, prevents garbage collection
    75  	// of blocks. When removed, all the referenced blocks are
    76  	// garbage-collected.
    77  	//
    78  	// TODO: We only really need to save a list of IDs, and not a
    79  	// full journal.
    80  	deferredGC *diskJournal
    81  
    82  	// s stores all the block data. s should always reflect the
    83  	// state you get by replaying all the entries in j.
    84  	s *blockDiskStore
    85  
    86  	aggregateInfo blockAggregateInfo
    87  }
    88  
    89  type blockOpType int
    90  
    91  const (
    92  	blockPutOp    blockOpType = 1
    93  	addRefOp      blockOpType = 2
    94  	removeRefsOp  blockOpType = 3
    95  	archiveRefsOp blockOpType = 4
    96  	mdRevMarkerOp blockOpType = 5
    97  )
    98  
    99  func (t blockOpType) String() string {
   100  	switch t {
   101  	case blockPutOp:
   102  		return "blockPut"
   103  	case addRefOp:
   104  		return "addReference"
   105  	case removeRefsOp:
   106  		return "removeReferences"
   107  	case archiveRefsOp:
   108  		return "archiveReferences"
   109  	case mdRevMarkerOp:
   110  		return "mdRevisionMarker"
   111  	default:
   112  		return fmt.Sprintf("blockOpType(%d)", t)
   113  	}
   114  }
   115  
   116  // A blockJournalEntry is just the name of the operation and the
   117  // associated block ID and contexts. Fields are exported only for
   118  // serialization.
   119  type blockJournalEntry struct {
   120  	// Must be one of the four ops above.
   121  	Op blockOpType
   122  	// Must have exactly one entry with one context for blockPutOp and
   123  	// addRefOp.  Used for all ops except for mdRevMarkerOp.
   124  	Contexts kbfsblock.ContextMap `codec:",omitempty"`
   125  	// Only used for mdRevMarkerOps.
   126  	Revision kbfsmd.Revision `codec:",omitempty"`
   127  	// Ignore this entry while flushing if this is true.
   128  	Ignore bool `codec:",omitempty"`
   129  	// If the current journal ID doesn't match this journal ID, then
   130  	// ignore this entry while flushing.
   131  	MDJournalID *kbfsmd.ID `codec:",omitempty"`
   132  	// This is an MD rev marker that represents a local squash.  TODO:
   133  	// combine this with Ignore using a more generic flags or state
   134  	// field, once we can change the journal format.
   135  	IsLocalSquash bool `codec:",omitempty"`
   136  	// This is legacy and only present for backwards compatibility.
   137  	// It can be removed as soon as we are sure there are no more
   138  	// journal entries in the wild with this set.
   139  	Unignorable bool `codec:",omitempty"`
   140  
   141  	codec.UnknownFieldSetHandler
   142  }
   143  
   144  // Get the single context stored in this entry. Only applicable to
   145  // blockPutOp and addRefOp.
   146  func (e blockJournalEntry) getSingleContext() (
   147  	kbfsblock.ID, kbfsblock.Context, error) {
   148  	switch e.Op {
   149  	case blockPutOp, addRefOp:
   150  		if len(e.Contexts) != 1 {
   151  			return kbfsblock.ID{}, kbfsblock.Context{}, errors.Errorf(
   152  				"Op %s doesn't have exactly one context: %v",
   153  				e.Op, e.Contexts)
   154  		}
   155  		for id, idContexts := range e.Contexts {
   156  			if len(idContexts) != 1 {
   157  				return kbfsblock.ID{}, kbfsblock.Context{}, errors.Errorf(
   158  					"Op %s doesn't have exactly one context for id=%s: %v",
   159  					e.Op, id, idContexts)
   160  			}
   161  			return id, idContexts[0], nil
   162  		}
   163  	}
   164  
   165  	return kbfsblock.ID{}, kbfsblock.Context{}, errors.Errorf(
   166  		"getSingleContext() erroneously called on op %s", e.Op)
   167  }
   168  
   169  func (e blockJournalEntry) ignore(mdJournalID kbfsmd.ID) bool {
   170  	return e.Ignore ||
   171  		(mdJournalID.IsValid() && e.MDJournalID != nil &&
   172  			mdJournalID != *e.MDJournalID)
   173  }
   174  
   175  func blockJournalDir(dir string) string {
   176  	return filepath.Join(dir, "block_journal")
   177  }
   178  
   179  func blockJournalStoreDir(dir string) string {
   180  	return filepath.Join(dir, "blocks")
   181  }
   182  
   183  func deferredGCBlockJournalDir(dir string) string {
   184  	return filepath.Join(dir, "gc_block_journal")
   185  }
   186  
   187  // makeBlockJournal returns a new blockJournal for the given
   188  // directory. Any existing journal entries are read.
   189  func makeBlockJournal(
   190  	ctx context.Context, codec kbfscodec.Codec, dir string,
   191  	log logger.Logger, vlog *libkb.VDebugLog) (*blockJournal, error) {
   192  	journalPath := blockJournalDir(dir)
   193  	deferLog := log.CloneWithAddedDepth(1)
   194  	j, err := makeDiskJournal(
   195  		codec, journalPath, reflect.TypeOf(blockJournalEntry{}))
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	gcJournalPath := deferredGCBlockJournalDir(dir)
   201  	gcj, err := makeDiskJournal(
   202  		codec, gcJournalPath, reflect.TypeOf(blockJournalEntry{}))
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  
   207  	storeDir := blockJournalStoreDir(dir)
   208  	s := makeBlockDiskStore(codec, storeDir)
   209  	journal := &blockJournal{
   210  		codec:      codec,
   211  		dir:        dir,
   212  		log:        traceLogger{log},
   213  		vlog:       vlog,
   214  		deferLog:   traceLogger{deferLog},
   215  		j:          j,
   216  		deferredGC: gcj,
   217  		s:          s,
   218  	}
   219  
   220  	// Get initial aggregate info.
   221  	err = kbfscodec.DeserializeFromFile(
   222  		codec, aggregateInfoPath(dir), &journal.aggregateInfo)
   223  	if !ioutil.IsNotExist(err) && err != nil {
   224  		return nil, err
   225  	}
   226  
   227  	return journal, nil
   228  }
   229  
   230  func (j *blockJournal) blockJournalFiles() []string {
   231  	return []string{
   232  		blockJournalDir(j.dir), deferredGCBlockJournalDir(j.dir),
   233  		blockJournalStoreDir(j.dir), aggregateInfoPath(j.dir),
   234  	}
   235  }
   236  
   237  // The functions below are for reading and writing aggregate info.
   238  
   239  // Ideally, this would be a JSON file, but we'd need a JSON
   240  // encoder/decoder that supports unknown fields.
   241  type blockAggregateInfo struct {
   242  	// StoredBytes counts the number of bytes of block data stored
   243  	// on disk.
   244  	StoredBytes int64
   245  	// StoredFiles counts an upper bound for the number of files
   246  	// of block data stored on disk.
   247  	StoredFiles int64
   248  	// UnflushedBytes counts the number of bytes of block data
   249  	// that is intended to be flushed to the server, but hasn't
   250  	// been yet. This should be always less than or equal to
   251  	// StoredBytes.
   252  	UnflushedBytes int64
   253  
   254  	codec.UnknownFieldSetHandler
   255  }
   256  
   257  func aggregateInfoPath(dir string) string {
   258  	return filepath.Join(dir, "block_aggregate_info")
   259  }
   260  
   261  // saturateAdd adds the given delta to the int64 at x; if the result
   262  // would be over MaxInt64, *x is instead set to MaxInt64, and if the
   263  // result would be negative, *x is instead set to 0. If *x is already
   264  // negative, *x is first set to 0 before doing the addition.
   265  func saturateAdd(x *int64, delta int64) {
   266  	if *x < 0 {
   267  		*x = 0
   268  	}
   269  
   270  	switch {
   271  	case delta > 0 && *x > (math.MaxInt64-delta):
   272  		*x = math.MaxInt64
   273  	case delta < 0 && *x+delta < 0:
   274  		*x = 0
   275  	default:
   276  		*x += delta
   277  	}
   278  }
   279  
   280  func (j *blockJournal) changeCounts(
   281  	deltaStoredBytes, deltaStoredFiles, deltaUnflushedBytes int64) error {
   282  	saturateAdd(&j.aggregateInfo.StoredBytes, deltaStoredBytes)
   283  	saturateAdd(&j.aggregateInfo.StoredFiles, deltaStoredFiles)
   284  	saturateAdd(&j.aggregateInfo.UnflushedBytes, deltaUnflushedBytes)
   285  	return kbfscodec.SerializeToFile(
   286  		j.codec, j.aggregateInfo, aggregateInfoPath(j.dir))
   287  }
   288  
   289  func (j *blockJournal) accumulateBlock(bytes, files int64) error {
   290  	if bytes < 0 {
   291  		panic("bytes unexpectedly negative")
   292  	}
   293  	if files < 0 {
   294  		panic("files unexpectedly negative")
   295  	}
   296  	return j.changeCounts(bytes, files, bytes)
   297  }
   298  
   299  func (j *blockJournal) flushBlock(bytes int64) error {
   300  	if bytes < 0 {
   301  		panic("bytes unexpectedly negative")
   302  	}
   303  	return j.changeCounts(0, 0, -bytes)
   304  }
   305  
   306  func (j *blockJournal) unstoreBlocks(bytes, files int64) error {
   307  	if bytes < 0 {
   308  		panic("bytes unexpectedly negative")
   309  	}
   310  	if files < 0 {
   311  		panic("files unexpectedly negative")
   312  	}
   313  	return j.changeCounts(-bytes, -files, 0)
   314  }
   315  
   316  // The functions below are for reading and writing journal entries.
   317  
   318  func (j *blockJournal) readJournalEntry(ordinal journalOrdinal) (
   319  	blockJournalEntry, error) {
   320  	entry, err := j.j.readJournalEntry(ordinal)
   321  	if err != nil {
   322  		return blockJournalEntry{}, err
   323  	}
   324  
   325  	return entry.(blockJournalEntry), nil
   326  }
   327  
   328  func (j *blockJournal) appendJournalEntry(
   329  	ctx context.Context, entry blockJournalEntry) (
   330  	journalOrdinal, error) {
   331  	ordinal, err := j.j.appendJournalEntry(nil, entry)
   332  	if err != nil {
   333  		return 0, err
   334  	}
   335  
   336  	return ordinal, nil
   337  }
   338  
   339  func (j *blockJournal) length() uint64 {
   340  	return j.j.length()
   341  }
   342  
   343  func (j *blockJournal) next() (journalOrdinal, error) {
   344  	last, err := j.j.readLatestOrdinal()
   345  	if ioutil.IsNotExist(err) {
   346  		return firstValidJournalOrdinal, nil
   347  	} else if err != nil {
   348  		return 0, err
   349  	}
   350  	return last + 1, nil
   351  }
   352  
   353  func (j *blockJournal) end() (journalOrdinal, error) {
   354  	last, err := j.j.readLatestOrdinal()
   355  	if ioutil.IsNotExist(err) {
   356  		return 0, nil
   357  	} else if err != nil {
   358  		return 0, err
   359  	}
   360  	return last + 1, nil
   361  }
   362  
   363  func (j *blockJournal) hasData(
   364  	ctx context.Context, id kbfsblock.ID) (bool, error) {
   365  	return j.s.hasData(ctx, id)
   366  }
   367  
   368  func (j *blockJournal) isUnflushed(
   369  	ctx context.Context, id kbfsblock.ID) (bool, error) {
   370  	return j.s.isUnflushed(ctx, id)
   371  }
   372  
   373  func (j *blockJournal) remove(ctx context.Context, id kbfsblock.ID) (
   374  	removedBytes, removedFiles int64, err error) {
   375  	bytesToRemove, err := j.s.getDataSize(ctx, id)
   376  	if err != nil {
   377  		return 0, 0, err
   378  	}
   379  
   380  	err = j.s.remove(ctx, id)
   381  	if err != nil {
   382  		return 0, 0, err
   383  	}
   384  
   385  	var filesToRemove int64
   386  	if bytesToRemove > 0 {
   387  		filesToRemove = filesPerBlockMax
   388  	}
   389  
   390  	return bytesToRemove, filesToRemove, nil
   391  }
   392  
   393  // All functions below are public functions.
   394  
   395  func (j *blockJournal) empty() bool {
   396  	return j.j.empty() && j.deferredGC.empty()
   397  }
   398  
   399  func (j *blockJournal) getDataWithContext(
   400  	ctx context.Context, id kbfsblock.ID, context kbfsblock.Context) (
   401  	[]byte, kbfscrypto.BlockCryptKeyServerHalf, error) {
   402  	return j.s.getDataWithContext(ctx, id, context)
   403  }
   404  
   405  func (j *blockJournal) getData(ctx context.Context, id kbfsblock.ID) (
   406  	[]byte, kbfscrypto.BlockCryptKeyServerHalf, error) {
   407  	return j.s.getData(ctx, id)
   408  }
   409  
   410  func (j *blockJournal) getDataSize(
   411  	ctx context.Context, id kbfsblock.ID) (int64, error) {
   412  	return j.s.getDataSize(ctx, id)
   413  }
   414  
   415  func (j *blockJournal) getStoredBytes() int64 {
   416  	return j.aggregateInfo.StoredBytes
   417  }
   418  
   419  func (j *blockJournal) getUnflushedBytes() int64 {
   420  	return j.aggregateInfo.UnflushedBytes
   421  }
   422  
   423  func (j *blockJournal) getStoredFiles() int64 {
   424  	return j.aggregateInfo.StoredFiles
   425  }
   426  
   427  // putBlockData puts the given block data. If err is non-nil, putData will
   428  // always be false.
   429  func (j *blockJournal) putBlockData(
   430  	ctx context.Context, id kbfsblock.ID, context kbfsblock.Context,
   431  	buf []byte, serverHalf kbfscrypto.BlockCryptKeyServerHalf) (
   432  	putData bool, err error) {
   433  	j.vlog.CLogf(
   434  		ctx, libkb.VLog1,
   435  		"Putting %d bytes of data for block %s with context %v",
   436  		len(buf), id, context)
   437  	defer func() {
   438  		if err != nil {
   439  			j.deferLog.CDebugf(ctx,
   440  				"Put for block %s with context %v failed with %+v",
   441  				id, context, err)
   442  		}
   443  	}()
   444  
   445  	putData, err = j.s.put(ctx, true, id, context, buf, serverHalf)
   446  	if err != nil {
   447  		return false, err
   448  	}
   449  
   450  	return putData, nil
   451  }
   452  
   453  // appendBlock appends an entry for the previously-put block to the
   454  // journal, and records the size for the put block.
   455  func (j *blockJournal) appendBlock(
   456  	ctx context.Context, id kbfsblock.ID, context kbfsblock.Context,
   457  	bufLenToAdd int64) error {
   458  	j.vlog.CLogf(ctx, libkb.VLog1, "Appending block %s to journal", id)
   459  
   460  	if bufLenToAdd > 0 {
   461  		var putFiles int64 = filesPerBlockMax
   462  		err := j.accumulateBlock(bufLenToAdd, putFiles)
   463  		if err != nil {
   464  			return err
   465  		}
   466  	}
   467  
   468  	next, err := j.next()
   469  	if err != nil {
   470  		return err
   471  	}
   472  
   473  	err = j.s.addReference(ctx, id, context, next.String())
   474  	if err != nil {
   475  		return err
   476  	}
   477  
   478  	_, err = j.appendJournalEntry(ctx, blockJournalEntry{
   479  		Op:       blockPutOp,
   480  		Contexts: kbfsblock.ContextMap{id: {context}},
   481  	})
   482  	return err
   483  }
   484  
   485  func (j *blockJournal) addReference(
   486  	ctx context.Context, id kbfsblock.ID, context kbfsblock.Context) (
   487  	err error) {
   488  	j.vlog.CLogf(
   489  		ctx, libkb.VLog1, "Adding reference for block %s with context %v",
   490  		id, context)
   491  	defer func() {
   492  		if err != nil {
   493  			j.deferLog.CDebugf(ctx,
   494  				"Adding reference for block %s with context %v failed with %+v",
   495  				id, context, err)
   496  		}
   497  	}()
   498  
   499  	next, err := j.next()
   500  	if err != nil {
   501  		return err
   502  	}
   503  
   504  	err = j.s.addReference(ctx, id, context, next.String())
   505  	if err != nil {
   506  		return err
   507  	}
   508  
   509  	_, err = j.appendJournalEntry(ctx, blockJournalEntry{
   510  		Op:       addRefOp,
   511  		Contexts: kbfsblock.ContextMap{id: {context}},
   512  	})
   513  	if err != nil {
   514  		return err
   515  	}
   516  
   517  	return nil
   518  }
   519  
   520  func (j *blockJournal) archiveReferences(
   521  	ctx context.Context, contexts kbfsblock.ContextMap) (err error) {
   522  	j.vlog.CLogf(ctx, libkb.VLog1, "Archiving references for %v", contexts)
   523  	defer func() {
   524  		if err != nil {
   525  			j.deferLog.CDebugf(ctx,
   526  				"Archiving references for %+v,", contexts, err)
   527  		}
   528  	}()
   529  
   530  	next, err := j.next()
   531  	if err != nil {
   532  		return err
   533  	}
   534  
   535  	err = j.s.archiveReferences(ctx, contexts, next.String())
   536  	if err != nil {
   537  		return err
   538  	}
   539  
   540  	_, err = j.appendJournalEntry(ctx, blockJournalEntry{
   541  		Op:       archiveRefsOp,
   542  		Contexts: contexts,
   543  	})
   544  	if err != nil {
   545  		return err
   546  	}
   547  
   548  	return nil
   549  }
   550  
   551  // removeReferences removes references for the given contexts from
   552  // their respective IDs.
   553  func (j *blockJournal) removeReferences(
   554  	ctx context.Context, contexts kbfsblock.ContextMap) (
   555  	liveCounts map[kbfsblock.ID]int, err error) {
   556  	j.vlog.CLogf(ctx, libkb.VLog1, "Removing references for %v", contexts)
   557  	defer func() {
   558  		if err != nil {
   559  			j.deferLog.CDebugf(ctx,
   560  				"Removing references for %+v", contexts, err)
   561  		}
   562  	}()
   563  
   564  	// Add the journal entry first, so that if we crash before
   565  	// removing the refs, we have at worst un-GCed blocks.
   566  
   567  	_, err = j.appendJournalEntry(ctx, blockJournalEntry{
   568  		Op:       removeRefsOp,
   569  		Contexts: contexts,
   570  	})
   571  	if err != nil {
   572  		return nil, err
   573  	}
   574  
   575  	liveCounts = make(map[kbfsblock.ID]int)
   576  	for id, idContexts := range contexts {
   577  		// Remove the references unconditionally here (i.e.,
   578  		// with an empty tag), since j.s should reflect the
   579  		// most recent state.
   580  		liveCount, err := j.s.removeReferences(ctx, id, idContexts, "")
   581  		if err != nil {
   582  			return nil, err
   583  		}
   584  
   585  		liveCounts[id] = liveCount
   586  	}
   587  
   588  	return liveCounts, nil
   589  }
   590  
   591  func (j *blockJournal) markMDRevision(ctx context.Context,
   592  	rev kbfsmd.Revision, journalID kbfsmd.ID, isPendingLocalSquash bool) (
   593  	err error) {
   594  	j.vlog.CLogf(
   595  		ctx, libkb.VLog1, "Marking MD revision %d in the block journal", rev)
   596  	defer func() {
   597  		if err != nil {
   598  			j.deferLog.CDebugf(ctx, "Marking MD revision %d error: %+v",
   599  				rev, err)
   600  		}
   601  	}()
   602  
   603  	_, err = j.appendJournalEntry(ctx, blockJournalEntry{
   604  		Op:          mdRevMarkerOp,
   605  		Revision:    rev,
   606  		MDJournalID: &journalID,
   607  		// If this MD represents a pending local squash, it should
   608  		// never be ignored since the revision it refers to can't be
   609  		// squashed again.
   610  		IsLocalSquash: isPendingLocalSquash,
   611  	})
   612  	if err != nil {
   613  		return err
   614  	}
   615  	return nil
   616  }
   617  
   618  // blockEntriesToFlush is an internal data structure for blockJournal;
   619  // its fields shouldn't be accessed outside this file.
   620  type blockEntriesToFlush struct {
   621  	all   []blockJournalEntry
   622  	first journalOrdinal
   623  
   624  	puts  blockPutState
   625  	adds  blockPutState
   626  	other []blockJournalEntry
   627  }
   628  
   629  func (be blockEntriesToFlush) length() int {
   630  	return len(be.all)
   631  }
   632  
   633  func (be blockEntriesToFlush) flushNeeded() bool {
   634  	return be.length() > 0
   635  }
   636  
   637  func (be blockEntriesToFlush) revIsLocalSquash(
   638  	rev kbfsmd.Revision, mdJournalID kbfsmd.ID) bool {
   639  	for _, entry := range be.other {
   640  		if !entry.ignore(mdJournalID) && entry.Op == mdRevMarkerOp &&
   641  			entry.Revision == rev {
   642  			return entry.IsLocalSquash || entry.Unignorable
   643  		}
   644  	}
   645  	return false
   646  }
   647  
   648  func (be blockEntriesToFlush) markFlushingBlockIDs(ids map[kbfsblock.ID]bool) {
   649  	for _, ptr := range be.puts.Ptrs() {
   650  		ids[ptr.ID] = true
   651  	}
   652  }
   653  
   654  func (be blockEntriesToFlush) clearFlushingBlockIDs(ids map[kbfsblock.ID]bool) {
   655  	for _, ptr := range be.puts.Ptrs() {
   656  		delete(ids, ptr.ID)
   657  	}
   658  }
   659  
   660  // Only entries with ordinals less than the given ordinal (assumed to
   661  // be <= latest ordinal + 1) are returned.  Also returns the maximum
   662  // MD revision that can be merged after the returned entries are
   663  // successfully flushed; if no entries are returned (i.e., the block
   664  // journal is empty) then any MD revision may be flushed even when
   665  // kbfsmd.RevisionUninitialized is returned.
   666  func (j *blockJournal) getNextEntriesToFlush(
   667  	ctx context.Context, end journalOrdinal, maxToFlush int,
   668  	mdJournalID kbfsmd.ID) (
   669  	entries blockEntriesToFlush, bytesToFlush int64,
   670  	maxMDRevToFlush kbfsmd.Revision, err error) {
   671  	first, err := j.j.readEarliestOrdinal()
   672  	if ioutil.IsNotExist(err) {
   673  		return blockEntriesToFlush{}, 0, kbfsmd.RevisionUninitialized, nil
   674  	} else if err != nil {
   675  		return blockEntriesToFlush{}, 0, kbfsmd.RevisionUninitialized, err
   676  	}
   677  
   678  	if first >= end {
   679  		return blockEntriesToFlush{}, 0, kbfsmd.RevisionUninitialized,
   680  			errors.Errorf("Trying to flush past the "+
   681  				"start of the journal (first=%d, end=%d)", first, end)
   682  	}
   683  
   684  	realEnd, err := j.end()
   685  	if realEnd == 0 {
   686  		return blockEntriesToFlush{}, 0, kbfsmd.RevisionUninitialized,
   687  			errors.Errorf("There was an earliest "+
   688  				"ordinal %d, but no latest ordinal", first)
   689  	} else if err != nil {
   690  		return blockEntriesToFlush{}, 0, kbfsmd.RevisionUninitialized, err
   691  	}
   692  
   693  	if end > realEnd {
   694  		return blockEntriesToFlush{}, 0, kbfsmd.RevisionUninitialized,
   695  			errors.Errorf("Trying to flush past the "+
   696  				"end of the journal (realEnd=%d, end=%d)", realEnd, end)
   697  	}
   698  
   699  	entries.puts = newBlockPutStateMemory(int(end - first))
   700  	entries.adds = newBlockPutStateMemory(int(end - first))
   701  	maxMDRevToFlush = kbfsmd.RevisionUninitialized
   702  
   703  	loopEnd := end
   704  	if first+journalOrdinal(maxToFlush) < end {
   705  		loopEnd = first + journalOrdinal(maxToFlush)
   706  	}
   707  
   708  	for ordinal := first; ordinal < loopEnd; ordinal++ {
   709  		entry, err := j.readJournalEntry(ordinal)
   710  		if err != nil {
   711  			return blockEntriesToFlush{}, 0, kbfsmd.RevisionUninitialized, err
   712  		}
   713  
   714  		if entry.ignore(mdJournalID) {
   715  			if loopEnd < end {
   716  				loopEnd++
   717  			}
   718  			entries.other = append(entries.other, entry)
   719  			entries.all = append(entries.all, entry)
   720  			continue
   721  		}
   722  
   723  		var blockData []byte
   724  		var serverHalf kbfscrypto.BlockCryptKeyServerHalf
   725  
   726  		switch entry.Op {
   727  		case blockPutOp:
   728  			id, bctx, err := entry.getSingleContext()
   729  			if err != nil {
   730  				return blockEntriesToFlush{}, 0,
   731  					kbfsmd.RevisionUninitialized, err
   732  			}
   733  
   734  			blockData, serverHalf, err = j.s.getData(ctx, id)
   735  			if err != nil {
   736  				return blockEntriesToFlush{}, 0,
   737  					kbfsmd.RevisionUninitialized, err
   738  			}
   739  			bytesToFlush += int64(len(blockData))
   740  
   741  			err = entries.puts.AddNewBlock(
   742  				ctx, data.BlockPointer{ID: id, Context: bctx},
   743  				nil, /* only used by folderBranchOps */
   744  				data.ReadyBlockData{
   745  					Buf:        blockData,
   746  					ServerHalf: serverHalf,
   747  				}, nil)
   748  			if err != nil {
   749  				return blockEntriesToFlush{}, 0,
   750  					kbfsmd.RevisionUninitialized, err
   751  			}
   752  
   753  		case addRefOp:
   754  			id, bctx, err := entry.getSingleContext()
   755  			if err != nil {
   756  				return blockEntriesToFlush{}, 0,
   757  					kbfsmd.RevisionUninitialized, err
   758  			}
   759  
   760  			err = entries.adds.AddNewBlock(
   761  				ctx, data.BlockPointer{ID: id, Context: bctx},
   762  				nil, /* only used by folderBranchOps */
   763  				data.ReadyBlockData{}, nil)
   764  			if err != nil {
   765  				return blockEntriesToFlush{}, 0,
   766  					kbfsmd.RevisionUninitialized, err
   767  			}
   768  
   769  		case mdRevMarkerOp:
   770  			if entry.Revision < maxMDRevToFlush {
   771  				return blockEntriesToFlush{}, 0, kbfsmd.RevisionUninitialized,
   772  					errors.Errorf("Max MD revision decreased in block journal "+
   773  						"from %d to %d", entry.Revision, maxMDRevToFlush)
   774  			}
   775  			maxMDRevToFlush = entry.Revision
   776  			entries.other = append(entries.other, entry)
   777  
   778  		default:
   779  			entries.other = append(entries.other, entry)
   780  		}
   781  
   782  		entries.all = append(entries.all, entry)
   783  	}
   784  	entries.first = first
   785  	return entries, bytesToFlush, maxMDRevToFlush, nil
   786  }
   787  
   788  // flushNonBPSBlockJournalEntry flushes journal entries that can't be
   789  // parallelized via a blockPutState.
   790  func flushNonBPSBlockJournalEntry(
   791  	ctx context.Context, log logger.Logger,
   792  	bserver BlockServer, tlfID tlf.ID, entry blockJournalEntry) error {
   793  	log.CDebugf(ctx, "Flushing other block op %v", entry)
   794  
   795  	switch entry.Op {
   796  	case removeRefsOp:
   797  		_, err := bserver.RemoveBlockReferences(
   798  			ctx, tlfID, entry.Contexts)
   799  		if err != nil {
   800  			return err
   801  		}
   802  
   803  	case archiveRefsOp:
   804  		err := bserver.ArchiveBlockReferences(
   805  			ctx, tlfID, entry.Contexts)
   806  		if err != nil {
   807  			return err
   808  		}
   809  
   810  	case blockPutOp:
   811  		if !entry.ignore(kbfsmd.ID{}) {
   812  			return errors.New("Trying to flush unignored blockPut as other")
   813  		}
   814  		// Otherwise nothing to do.
   815  
   816  	case mdRevMarkerOp:
   817  		// Nothing to do.
   818  
   819  	default:
   820  		return errors.Errorf("Unknown op %s", entry.Op)
   821  	}
   822  
   823  	return nil
   824  }
   825  
   826  func flushBlockEntries(ctx context.Context, log, deferLog traceLogger,
   827  	bserver BlockServer, bcache data.BlockCache, reporter Reporter, tlfID tlf.ID,
   828  	tlfName tlf.CanonicalName, entries blockEntriesToFlush,
   829  	cacheType DiskBlockCacheType) error {
   830  	if !entries.flushNeeded() {
   831  		// Avoid logging anything when there's nothing to flush.
   832  		return nil
   833  	}
   834  
   835  	// Do all the put state stuff first, in parallel.  We need to do
   836  	// the puts strictly before the addRefs, since the latter might
   837  	// reference the former.
   838  	log.CDebugf(ctx, "Putting %d blocks", entries.puts.numBlocks())
   839  	blocksToRemove, err := doBlockPuts(ctx, bserver, bcache, reporter,
   840  		log, deferLog, tlfID, tlfName, entries.puts, cacheType)
   841  	if err != nil {
   842  		if isRecoverableBlockError(err) {
   843  			log.CWarningf(ctx,
   844  				"Recoverable block error encountered on puts: %+v, ptrs=%v",
   845  				err, blocksToRemove)
   846  		}
   847  		return err
   848  	}
   849  
   850  	// Next, do the addrefs.
   851  	log.CDebugf(ctx, "Adding %d block references", entries.adds.numBlocks())
   852  	blocksToRemove, err = doBlockPuts(ctx, bserver, bcache, reporter,
   853  		log, deferLog, tlfID, tlfName, entries.adds, cacheType)
   854  	if err != nil {
   855  		if isRecoverableBlockError(err) {
   856  			log.CWarningf(ctx,
   857  				"Recoverable block error encountered on addRefs: %+v, ptrs=%v",
   858  				err, blocksToRemove)
   859  		}
   860  		return err
   861  	}
   862  
   863  	// Now do all the other, non-put/addref entries.  TODO:
   864  	// parallelize these as well.
   865  	for _, entry := range entries.other {
   866  		err := flushNonBPSBlockJournalEntry(ctx, log, bserver, tlfID, entry)
   867  		if err != nil {
   868  			return err
   869  		}
   870  	}
   871  
   872  	return nil
   873  }
   874  
   875  func (j *blockJournal) removeFlushedEntry(ctx context.Context,
   876  	ordinal journalOrdinal, entry blockJournalEntry) (
   877  	flushedBytes int64, err error) {
   878  	earliestOrdinal, err := j.j.readEarliestOrdinal()
   879  	if err != nil {
   880  		return 0, err
   881  	}
   882  
   883  	if ordinal != earliestOrdinal {
   884  		return 0, errors.Errorf("Expected ordinal %d, got %d",
   885  			ordinal, earliestOrdinal)
   886  	}
   887  
   888  	// Store the block byte count if we've finished a Put.
   889  	if entry.Op == blockPutOp && !entry.ignore(kbfsmd.ID{}) {
   890  		id, _, err := entry.getSingleContext()
   891  		if err != nil {
   892  			return 0, err
   893  		}
   894  
   895  		err = j.s.markFlushed(ctx, id)
   896  		if err != nil {
   897  			return 0, err
   898  		}
   899  
   900  		flushedBytes, err = j.s.getDataSize(ctx, id)
   901  		if err != nil {
   902  			return 0, err
   903  		}
   904  
   905  		err = j.flushBlock(flushedBytes)
   906  		if err != nil {
   907  			return 0, err
   908  		}
   909  	}
   910  
   911  	// Remove any of the entry's refs that hasn't been modified by
   912  	// a subsequent block op (i.e., that has earliestOrdinal as a
   913  	// tag). Has no effect for removeRefsOp (since those are
   914  	// already removed) or mdRevMarkerOp (which has no
   915  	// references).
   916  	for id, idContexts := range entry.Contexts {
   917  		liveCount, err := j.s.removeReferences(
   918  			ctx, id, idContexts, earliestOrdinal.String())
   919  		if err != nil {
   920  			return 0, err
   921  		}
   922  		// Postpone garbage collection until the next MD flush.
   923  		if liveCount == 0 {
   924  			_, err := j.deferredGC.appendJournalEntry(nil, entry)
   925  			if err != nil {
   926  				return 0, err
   927  			}
   928  		}
   929  	}
   930  
   931  	_, err = j.j.removeEarliest()
   932  	if err != nil {
   933  		return 0, err
   934  	}
   935  
   936  	return flushedBytes, nil
   937  }
   938  
   939  func (j *blockJournal) removeFlushedEntries(ctx context.Context,
   940  	entries blockEntriesToFlush, tlfID tlf.ID, reporter Reporter) (
   941  	totalFlushedBytes int64, err error) {
   942  	// Remove them all!
   943  	for i, entry := range entries.all {
   944  		flushedBytes, err := j.removeFlushedEntry(
   945  			ctx, entries.first+journalOrdinal(i), entry)
   946  		if err != nil {
   947  			return 0, err
   948  		}
   949  		totalFlushedBytes += flushedBytes
   950  
   951  		reporter.NotifySyncStatus(ctx, &keybase1.FSPathSyncStatus{
   952  			FolderType: tlfID.Type().FolderType(),
   953  			// Path: TODO,
   954  			// SyncingBytes: TODO,
   955  			// SyncingOps: TODO,
   956  			SyncedBytes: flushedBytes,
   957  		})
   958  	}
   959  
   960  	// The block journal might be empty, but deferredGC might
   961  	// still be non-empty, so we have to wait for that to be empty
   962  	// before nuking the whole journal (see clearDeferredGCRange).
   963  
   964  	return totalFlushedBytes, nil
   965  }
   966  
   967  func (j *blockJournal) ignoreBlocksAndMDRevMarkersInJournal(ctx context.Context,
   968  	idsToIgnore map[kbfsblock.ID]bool, rev kbfsmd.Revision,
   969  	dj *diskJournal) (totalIgnoredBytes int64, err error) {
   970  	first, err := dj.readEarliestOrdinal()
   971  	if ioutil.IsNotExist(err) {
   972  		return 0, nil
   973  	} else if err != nil {
   974  		return 0, err
   975  	}
   976  	last, err := dj.readLatestOrdinal()
   977  	if err != nil {
   978  		return 0, err
   979  	}
   980  
   981  	isMainJournal := dj.dir == j.j.dir
   982  
   983  	// Iterate backwards since the blocks to ignore are likely to be
   984  	// at the end of the journal.
   985  	ignored := 0
   986  	ignoredRev := false
   987  	// i is unsigned, so make sure to handle overflow when `first` is
   988  	// 0 by checking that it's less than `last`.  TODO: handle
   989  	// first==0 and last==maxuint?
   990  	for i := last; i >= first && i <= last; i-- {
   991  		entry, err := dj.readJournalEntry(i)
   992  		if err != nil {
   993  			// If we can't read a particular entry while ignoring old
   994  			// revisions, the entry might be corrupt.  But returning
   995  			// an error is harsh and dangerous because at this point a
   996  			// new MD revision has already been appended to the MD
   997  			// journal; if we error to the caller then they won't
   998  			// write another marker and on a restart the whole MD
   999  			// journal can be flushed without waiting for the
  1000  			// corresponding blocks to flush.  See HOTPOT-193.  So
  1001  			// instead, log the error and keep going in that case.  If
  1002  			// the entry continues to be unreadable during flush, then
  1003  			// the journal (and eventually another round of conflict
  1004  			// resolution) will become stuck and the error will
  1005  			// surface up to the user.
  1006  			j.log.CWarningf(ctx, "Couldn't read journal entry %d: %+v", i, err)
  1007  			continue
  1008  		}
  1009  		e := entry.(blockJournalEntry)
  1010  
  1011  		switch e.Op {
  1012  		case blockPutOp, addRefOp:
  1013  			id, _, err := e.getSingleContext()
  1014  			if err != nil {
  1015  				return 0, err
  1016  			}
  1017  
  1018  			if !idsToIgnore[id] {
  1019  				continue
  1020  			}
  1021  			ignored++
  1022  
  1023  			e.Ignore = true
  1024  			err = dj.writeJournalEntry(i, e)
  1025  			if err != nil {
  1026  				return 0, err
  1027  			}
  1028  
  1029  			if e.Op == blockPutOp && isMainJournal {
  1030  				// Treat ignored put ops as flushed
  1031  				// for the purposes of accounting.
  1032  				ignoredBytes, err := j.s.getDataSize(ctx, id)
  1033  				if err != nil {
  1034  					return 0, err
  1035  				}
  1036  
  1037  				err = j.flushBlock(ignoredBytes)
  1038  				if err != nil {
  1039  					return 0, err
  1040  				}
  1041  
  1042  				totalIgnoredBytes += ignoredBytes
  1043  			}
  1044  
  1045  		case mdRevMarkerOp:
  1046  			if ignoredRev {
  1047  				continue
  1048  			}
  1049  
  1050  			e.Ignore = true
  1051  			err = dj.writeJournalEntry(i, e)
  1052  			if err != nil {
  1053  				return 0, err
  1054  			}
  1055  
  1056  			// We must ignore all the way up to the MD marker that
  1057  			// matches the revision of the squash, otherwise we may
  1058  			// put the new squash MD before all the blocks have been
  1059  			// put.
  1060  			if e.Revision == rev {
  1061  				ignoredRev = true
  1062  			}
  1063  		}
  1064  
  1065  		// If we've ignored all of the block IDs in `idsToIgnore`, and
  1066  		// the earliest md marker we care about, we can avoid
  1067  		// iterating through the rest of the journal.
  1068  		if len(idsToIgnore) == ignored && ignoredRev {
  1069  			break
  1070  		}
  1071  	}
  1072  
  1073  	return totalIgnoredBytes, nil
  1074  }
  1075  
  1076  func (j *blockJournal) ignoreBlocksAndMDRevMarkers(ctx context.Context,
  1077  	blocksToIgnore []kbfsblock.ID, rev kbfsmd.Revision) (
  1078  	totalIgnoredBytes int64, err error) {
  1079  	idsToIgnore := make(map[kbfsblock.ID]bool)
  1080  	for _, id := range blocksToIgnore {
  1081  		idsToIgnore[id] = true
  1082  	}
  1083  
  1084  	return j.ignoreBlocksAndMDRevMarkersInJournal(
  1085  		ctx, idsToIgnore, rev, j.j)
  1086  }
  1087  
  1088  // getDeferredRange gets the earliest and latest revision of the
  1089  // deferred GC journal.  If the returned length is 0, there's no need
  1090  // for further GC.
  1091  func (j *blockJournal) getDeferredGCRange() (
  1092  	len int, earliest, latest journalOrdinal, err error) {
  1093  	earliest, err = j.deferredGC.readEarliestOrdinal()
  1094  	if ioutil.IsNotExist(err) {
  1095  		return 0, 0, 0, nil
  1096  	} else if err != nil {
  1097  		return 0, 0, 0, err
  1098  	}
  1099  
  1100  	latest, err = j.deferredGC.readLatestOrdinal()
  1101  	if ioutil.IsNotExist(err) {
  1102  		return 0, 0, 0, nil
  1103  	} else if err != nil {
  1104  		return 0, 0, 0, err
  1105  	}
  1106  
  1107  	return int(latest - earliest + 1), earliest, latest, nil
  1108  }
  1109  
  1110  // doGC collects any unreferenced blocks from flushed
  1111  // entries. earliest and latest should be from a call to
  1112  // getDeferredGCRange, and clearDeferredGCRange should be called after
  1113  // this function. This function only reads the deferred GC journal at
  1114  // the given range and reads/writes the block store, so callers may
  1115  // use that to relax any synchronization requirements.
  1116  func (j *blockJournal) doGC(ctx context.Context,
  1117  	earliest, latest journalOrdinal) (
  1118  	removedBytes, removedFiles int64, err error) {
  1119  	// Safe to check the earliest ordinal, even if the caller is using
  1120  	// relaxed synchronization, since this is the only function that
  1121  	// removes items from the deferred journal.
  1122  	first, err := j.deferredGC.readEarliestOrdinal()
  1123  	if err != nil {
  1124  		return 0, 0, err
  1125  	}
  1126  	if first != earliest {
  1127  		return 0, 0, errors.Errorf("Expected deferred earliest %d, "+
  1128  			"but actual earliest is %d", earliest, first)
  1129  	}
  1130  
  1131  	// Delete the block data for anything in the GC journal.
  1132  	j.vlog.CLogf(
  1133  		ctx, libkb.VLog1, "Garbage-collecting blocks for entries [%d, %d]",
  1134  		earliest, latest)
  1135  	for i := earliest; i <= latest; i++ {
  1136  		e, err := j.deferredGC.readJournalEntry(i)
  1137  		if err != nil {
  1138  			return 0, 0, err
  1139  		}
  1140  
  1141  		entry, ok := e.(blockJournalEntry)
  1142  		if !ok {
  1143  			return 0, 0, errors.New("Unexpected block journal entry type to GC")
  1144  		}
  1145  
  1146  		for id := range entry.Contexts {
  1147  			// TODO: once we support references, this needs to be made
  1148  			// goroutine-safe.
  1149  			hasRef, err := j.s.hasAnyRef(ctx, id)
  1150  			if err != nil {
  1151  				return 0, 0, err
  1152  			}
  1153  			if !hasRef {
  1154  				// Garbage-collect the old entry.
  1155  				idRemovedBytes, idRemovedFiles, err :=
  1156  					j.remove(ctx, id)
  1157  				if err != nil {
  1158  					return 0, 0, err
  1159  				}
  1160  				removedBytes += idRemovedBytes
  1161  				removedFiles += idRemovedFiles
  1162  			}
  1163  		}
  1164  	}
  1165  
  1166  	return removedBytes, removedFiles, nil
  1167  }
  1168  
  1169  // clearDeferredGCRange removes the given range from the deferred
  1170  // journal. If the journal goes completely empty, it then nukes the
  1171  // journal directories.
  1172  func (j *blockJournal) clearDeferredGCRange(
  1173  	ctx context.Context, removedBytes, removedFiles int64,
  1174  	earliest, latest journalOrdinal) (
  1175  	clearedJournal bool, aggregateInfo blockAggregateInfo,
  1176  	err error) {
  1177  	for i := earliest; i <= latest; i++ {
  1178  		_, err := j.deferredGC.removeEarliest()
  1179  		if err != nil {
  1180  			return false, blockAggregateInfo{}, err
  1181  		}
  1182  	}
  1183  
  1184  	// If we crash before calling this, the journal bytes/files
  1185  	// counts will be inaccurate. But this will be resolved when
  1186  	// the journal goes empty in the clause above.
  1187  	err = j.unstoreBlocks(removedBytes, removedFiles)
  1188  	if err != nil {
  1189  		return false, blockAggregateInfo{}, err
  1190  	}
  1191  
  1192  	aggregateInfo = j.aggregateInfo
  1193  
  1194  	if j.empty() {
  1195  		j.log.CDebugf(ctx, "Block journal is now empty")
  1196  
  1197  		j.aggregateInfo = blockAggregateInfo{}
  1198  
  1199  		err = j.s.clear()
  1200  		if err != nil {
  1201  			return false, blockAggregateInfo{}, err
  1202  		}
  1203  
  1204  		for _, dir := range j.blockJournalFiles() {
  1205  			j.log.CDebugf(ctx, "Removing all files in %s", dir)
  1206  			err := ioutil.RemoveAll(dir)
  1207  			if err != nil {
  1208  				return false, blockAggregateInfo{}, err
  1209  			}
  1210  		}
  1211  
  1212  		clearedJournal = true
  1213  	}
  1214  
  1215  	return clearedJournal, aggregateInfo, nil
  1216  }
  1217  
  1218  func (j *blockJournal) getAllRefsForTest() (map[kbfsblock.ID]blockRefMap, error) {
  1219  	refs := make(map[kbfsblock.ID]blockRefMap)
  1220  
  1221  	first, err := j.j.readEarliestOrdinal()
  1222  	if ioutil.IsNotExist(err) {
  1223  		return refs, nil
  1224  	} else if err != nil {
  1225  		return nil, err
  1226  	}
  1227  	last, err := j.j.readLatestOrdinal()
  1228  	if err != nil {
  1229  		return nil, err
  1230  	}
  1231  
  1232  	for i := first; i <= last; i++ {
  1233  		e, err := j.readJournalEntry(i)
  1234  		if err != nil {
  1235  			return nil, err
  1236  		}
  1237  
  1238  		// Handle single ops separately.
  1239  		switch e.Op {
  1240  		case blockPutOp, addRefOp:
  1241  			id, context, err := e.getSingleContext()
  1242  			if err != nil {
  1243  				return nil, err
  1244  			}
  1245  
  1246  			blockRefs := refs[id]
  1247  			if blockRefs == nil {
  1248  				blockRefs = make(blockRefMap)
  1249  				refs[id] = blockRefs
  1250  			}
  1251  
  1252  			err = blockRefs.put(context, liveBlockRef, i.String())
  1253  			if err != nil {
  1254  				return nil, err
  1255  			}
  1256  
  1257  			continue
  1258  		}
  1259  
  1260  		for id, idContexts := range e.Contexts {
  1261  			blockRefs := refs[id]
  1262  
  1263  			switch e.Op {
  1264  			case removeRefsOp:
  1265  				if blockRefs == nil {
  1266  					// All refs are already gone,
  1267  					// which is not an error.
  1268  					continue
  1269  				}
  1270  
  1271  				for _, context := range idContexts {
  1272  					err := blockRefs.remove(context, "")
  1273  					if err != nil {
  1274  						return nil, err
  1275  					}
  1276  				}
  1277  
  1278  				if len(blockRefs) == 0 {
  1279  					delete(refs, id)
  1280  				}
  1281  
  1282  			case archiveRefsOp:
  1283  				if blockRefs == nil {
  1284  					blockRefs = make(blockRefMap)
  1285  					refs[id] = blockRefs
  1286  				}
  1287  
  1288  				for _, context := range idContexts {
  1289  					err := blockRefs.put(
  1290  						context, archivedBlockRef, i.String())
  1291  					if err != nil {
  1292  						return nil, err
  1293  					}
  1294  				}
  1295  
  1296  			case mdRevMarkerOp:
  1297  				// Ignore MD revision markers.
  1298  				continue
  1299  
  1300  			default:
  1301  				return nil, errors.Errorf("Unknown op %s", e.Op)
  1302  			}
  1303  		}
  1304  	}
  1305  	return refs, nil
  1306  }
  1307  
  1308  func (j *blockJournal) markLatestRevMarkerAsLocalSquash(
  1309  	mdJournalID kbfsmd.ID) error {
  1310  	first, err := j.j.readEarliestOrdinal()
  1311  	if ioutil.IsNotExist(err) {
  1312  		return nil
  1313  	} else if err != nil {
  1314  		return err
  1315  	}
  1316  	last, err := j.j.readLatestOrdinal()
  1317  	if err != nil {
  1318  		return err
  1319  	}
  1320  
  1321  	// Iterate backwards to find the latest md marker.
  1322  	for i := last; i >= first && i <= last; i-- {
  1323  		entry, err := j.j.readJournalEntry(i)
  1324  		if err != nil {
  1325  			return err
  1326  		}
  1327  		e := entry.(blockJournalEntry)
  1328  		if e.ignore(mdJournalID) || e.Op != mdRevMarkerOp {
  1329  			continue
  1330  		}
  1331  
  1332  		e.IsLocalSquash = true
  1333  		return j.j.writeJournalEntry(i, e)
  1334  	}
  1335  
  1336  	return errors.Errorf("Couldn't find an md rev marker between %d and %d",
  1337  		first, last)
  1338  }
  1339  
  1340  func (j *blockJournal) checkInSyncForTest() error {
  1341  	journalRefs, err := j.getAllRefsForTest()
  1342  	if err != nil {
  1343  		return err
  1344  	}
  1345  
  1346  	storeRefs, err := j.s.getAllRefsForTest()
  1347  	if err != nil {
  1348  		return err
  1349  	}
  1350  
  1351  	if !reflect.DeepEqual(journalRefs, storeRefs) {
  1352  		return errors.Errorf("journal refs = %+v != store refs = %+v",
  1353  			journalRefs, storeRefs)
  1354  	}
  1355  	return nil
  1356  }