github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/block_ops.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  	"time"
     9  
    10  	"github.com/keybase/client/go/kbfs/data"
    11  	"github.com/keybase/client/go/kbfs/env"
    12  	"github.com/keybase/client/go/kbfs/kbfsblock"
    13  	"github.com/keybase/client/go/kbfs/libkey"
    14  	"github.com/keybase/client/go/kbfs/tlf"
    15  	"github.com/keybase/client/go/protocol/keybase1"
    16  	"github.com/pkg/errors"
    17  	"golang.org/x/net/context"
    18  )
    19  
    20  type blockOpsConfig interface {
    21  	data.Versioner
    22  	logMaker
    23  	blockCacher
    24  	blockServerGetter
    25  	codecGetter
    26  	cryptoPureGetter
    27  	keyGetterGetter
    28  	diskBlockCacheGetter
    29  	syncedTlfGetterSetter
    30  	initModeGetter
    31  	blockCryptVersioner
    32  	clockGetter
    33  	reporterGetter
    34  	settingsDBGetter
    35  	subscriptionManagerGetter
    36  	subscriptionManagerPublisherGetter
    37  }
    38  
    39  // BlockOpsStandard implements the BlockOps interface by relaying
    40  // requests to the block server.
    41  type BlockOpsStandard struct {
    42  	config blockOpsConfig
    43  	log    traceLogger
    44  	queue  *blockRetrievalQueue
    45  }
    46  
    47  var _ BlockOps = (*BlockOpsStandard)(nil)
    48  
    49  // NewBlockOpsStandard creates a new BlockOpsStandard
    50  func NewBlockOpsStandard(
    51  	config blockOpsConfig, queueSize, prefetchQueueSize int,
    52  	throttledPrefetchPeriod time.Duration,
    53  	appStateUpdater env.AppStateUpdater) *BlockOpsStandard {
    54  	bg := &realBlockGetter{config: config}
    55  	qConfig := &realBlockRetrievalConfig{
    56  		blockRetrievalPartialConfig: config,
    57  		bg:                          bg,
    58  	}
    59  	q := newBlockRetrievalQueue(
    60  		queueSize, prefetchQueueSize, throttledPrefetchPeriod, qConfig,
    61  		appStateUpdater)
    62  	bops := &BlockOpsStandard{
    63  		config: config,
    64  		log:    traceLogger{config.MakeLogger("")},
    65  		queue:  q,
    66  	}
    67  	return bops
    68  }
    69  
    70  // Get implements the BlockOps interface for BlockOpsStandard.
    71  func (b *BlockOpsStandard) Get(ctx context.Context, kmd libkey.KeyMetadata,
    72  	blockPtr data.BlockPointer, block data.Block,
    73  	lifetime data.BlockCacheLifetime, branch data.BranchName) error {
    74  	// Check the journal explicitly first, so we don't get stuck in
    75  	// the block-fetching queue.
    76  	if journalBServer, ok := b.config.BlockServer().(journalBlockServer); ok {
    77  		data, serverHalf, found, err := journalBServer.getBlockFromJournal(
    78  			ctx, kmd.TlfID(), blockPtr.ID)
    79  		if err != nil {
    80  			return err
    81  		}
    82  		if found {
    83  			return assembleBlockLocal(
    84  				ctx, b.config.keyGetter(), b.config.Codec(),
    85  				b.config.cryptoPure(), kmd, blockPtr, block, data, serverHalf)
    86  		}
    87  	}
    88  
    89  	b.log.LazyTrace(ctx, "BOps: Requesting %s", blockPtr.ID)
    90  
    91  	action := b.config.Mode().DefaultBlockRequestAction()
    92  	if branch != data.MasterBranch {
    93  		action = action.AddNonMasterBranch()
    94  	}
    95  	errCh := b.queue.Request(
    96  		ctx, defaultOnDemandRequestPriority, kmd,
    97  		blockPtr, block, lifetime, action)
    98  	err := <-errCh
    99  
   100  	b.log.LazyTrace(ctx, "BOps: Request fulfilled for %s (err=%v)", blockPtr.ID, err)
   101  
   102  	return err
   103  }
   104  
   105  // GetEncodedSizes implements the BlockOps interface for
   106  // BlockOpsStandard.
   107  func (b *BlockOpsStandard) GetEncodedSizes(
   108  	ctx context.Context, kmd libkey.KeyMetadata,
   109  	blockPtrs []data.BlockPointer) (
   110  	sizes []uint32, statuses []keybase1.BlockStatus, err error) {
   111  	// Check the journal explicitly first, so we don't get stuck in
   112  	// the block-fetching queue.
   113  
   114  	var ids []kbfsblock.ID
   115  	var contexts []kbfsblock.Context
   116  	var indices []int
   117  	sizes = make([]uint32, len(blockPtrs))
   118  	statuses = make([]keybase1.BlockStatus, len(blockPtrs))
   119  
   120  	for i, blockPtr := range blockPtrs {
   121  		if journalBServer, ok := b.config.BlockServer().(journalBlockServer); ok {
   122  			size, found, err := journalBServer.getBlockSizeFromJournal(
   123  				ctx, kmd.TlfID(), blockPtr.ID)
   124  			if err != nil {
   125  				return nil, nil, err
   126  			}
   127  			if found && size > 0 {
   128  				sizes[i] = size
   129  				statuses[i] = keybase1.BlockStatus_LIVE
   130  				continue
   131  			}
   132  		}
   133  
   134  		// Not in journal.
   135  		ids = append(ids, blockPtr.ID)
   136  		contexts = append(contexts, blockPtr.Context)
   137  		indices = append(indices, i)
   138  	}
   139  
   140  	if len(ids) == 0 {
   141  		return sizes, statuses, nil
   142  	}
   143  
   144  	servSizes, servStatuses, err := b.config.BlockServer().GetEncodedSizes(
   145  		ctx, kmd.TlfID(), ids, contexts)
   146  	if err != nil {
   147  		return nil, nil, err
   148  	}
   149  
   150  	for i, j := range indices {
   151  		sizes[j] = servSizes[i]
   152  		statuses[j] = servStatuses[i]
   153  	}
   154  	return sizes, statuses, nil
   155  }
   156  
   157  // Ready implements the BlockOps interface for BlockOpsStandard.
   158  func (b *BlockOpsStandard) Ready(ctx context.Context, kmd libkey.KeyMetadata,
   159  	block data.Block) (id kbfsblock.ID, plainSize int, readyBlockData data.ReadyBlockData,
   160  	err error) {
   161  	defer func() {
   162  		if err != nil {
   163  			id = kbfsblock.ID{}
   164  			plainSize = 0
   165  			readyBlockData = data.ReadyBlockData{}
   166  		}
   167  	}()
   168  
   169  	crypto := b.config.cryptoPure()
   170  
   171  	tlfCryptKey, err := b.config.keyGetter().
   172  		GetTLFCryptKeyForEncryption(ctx, kmd)
   173  	if err != nil {
   174  		return
   175  	}
   176  
   177  	// New server key half for the block.
   178  	serverHalf, err := crypto.MakeRandomBlockCryptKeyServerHalf()
   179  	if err != nil {
   180  		return
   181  	}
   182  
   183  	plainSize, encryptedBlock, err := crypto.EncryptBlock(
   184  		block, tlfCryptKey, serverHalf)
   185  	if err != nil {
   186  		return
   187  	}
   188  
   189  	buf, err := b.config.Codec().Encode(encryptedBlock)
   190  	if err != nil {
   191  		return
   192  	}
   193  
   194  	readyBlockData = data.ReadyBlockData{
   195  		Buf:        buf,
   196  		ServerHalf: serverHalf,
   197  	}
   198  
   199  	encodedSize := readyBlockData.GetEncodedSize()
   200  	if encodedSize < plainSize {
   201  		err = TooLowByteCountError{
   202  			ExpectedMinByteCount: plainSize,
   203  			ByteCount:            encodedSize,
   204  		}
   205  		return
   206  	}
   207  
   208  	id, err = kbfsblock.MakePermanentID(buf, encryptedBlock.Version)
   209  	if err != nil {
   210  		return
   211  	}
   212  
   213  	// Cache the encoded size.
   214  	block.SetEncodedSize(uint32(encodedSize))
   215  
   216  	return
   217  }
   218  
   219  // Delete implements the BlockOps interface for BlockOpsStandard.
   220  func (b *BlockOpsStandard) Delete(ctx context.Context, tlfID tlf.ID,
   221  	ptrs []data.BlockPointer) (liveCounts map[kbfsblock.ID]int, err error) {
   222  	contexts := make(kbfsblock.ContextMap)
   223  	for _, ptr := range ptrs {
   224  		contexts[ptr.ID] = append(contexts[ptr.ID], ptr.Context)
   225  	}
   226  	return b.config.BlockServer().RemoveBlockReferences(ctx, tlfID, contexts)
   227  }
   228  
   229  // Archive implements the BlockOps interface for BlockOpsStandard.
   230  func (b *BlockOpsStandard) Archive(ctx context.Context, tlfID tlf.ID,
   231  	ptrs []data.BlockPointer) error {
   232  	contexts := make(kbfsblock.ContextMap)
   233  	for _, ptr := range ptrs {
   234  		contexts[ptr.ID] = append(contexts[ptr.ID], ptr.Context)
   235  	}
   236  
   237  	return b.config.BlockServer().ArchiveBlockReferences(ctx, tlfID, contexts)
   238  }
   239  
   240  // GetLiveCount implements the BlockOps interface for BlockOpsStandard.
   241  func (b *BlockOpsStandard) GetLiveCount(
   242  	ctx context.Context, tlfID tlf.ID, ptrs []data.BlockPointer) (
   243  	liveCounts map[kbfsblock.ID]int, err error) {
   244  	contexts := make(kbfsblock.ContextMap)
   245  	for _, ptr := range ptrs {
   246  		contexts[ptr.ID] = append(contexts[ptr.ID], ptr.Context)
   247  	}
   248  
   249  	return b.config.BlockServer().GetLiveBlockReferences(ctx, tlfID, contexts)
   250  }
   251  
   252  // TogglePrefetcher implements the BlockOps interface for BlockOpsStandard.
   253  func (b *BlockOpsStandard) TogglePrefetcher(enable bool) <-chan struct{} {
   254  	return b.queue.TogglePrefetcher(enable, nil, nil)
   255  }
   256  
   257  // Prefetcher implements the BlockOps interface for BlockOpsStandard.
   258  func (b *BlockOpsStandard) Prefetcher() Prefetcher {
   259  	return b.queue.Prefetcher()
   260  }
   261  
   262  // BlockRetriever implements the BlockOps interface for BlockOpsStandard.
   263  func (b *BlockOpsStandard) BlockRetriever() BlockRetriever {
   264  	return b.queue
   265  }
   266  
   267  // Shutdown implements the BlockOps interface for BlockOpsStandard.
   268  func (b *BlockOpsStandard) Shutdown(ctx context.Context) error {
   269  	// Block on the queue being done.
   270  	select {
   271  	case <-b.queue.Shutdown():
   272  		return nil
   273  	case <-ctx.Done():
   274  		return errors.WithStack(ctx.Err())
   275  	}
   276  }