github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/bserver_memory.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  	"errors"
     9  	"fmt"
    10  	"math"
    11  	"sync"
    12  
    13  	"github.com/keybase/client/go/kbfs/kbfsblock"
    14  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    15  	"github.com/keybase/client/go/kbfs/tlf"
    16  	"github.com/keybase/client/go/logger"
    17  	"github.com/keybase/client/go/protocol/keybase1"
    18  	"golang.org/x/net/context"
    19  )
    20  
    21  type blockMemEntry struct {
    22  	tlfID         tlf.ID
    23  	blockData     []byte
    24  	keyServerHalf kbfscrypto.BlockCryptKeyServerHalf
    25  	refs          blockRefMap
    26  }
    27  
    28  // BlockServerMemory implements the BlockServer interface by just
    29  // storing blocks in memory.
    30  type BlockServerMemory struct {
    31  	log logger.Logger
    32  
    33  	lock sync.RWMutex
    34  	// m is nil after Shutdown() is called.
    35  	m map[kbfsblock.ID]blockMemEntry
    36  }
    37  
    38  var _ blockServerLocal = (*BlockServerMemory)(nil)
    39  
    40  // NewBlockServerMemory constructs a new BlockServerMemory that stores
    41  // its data in memory.
    42  func NewBlockServerMemory(log logger.Logger) *BlockServerMemory {
    43  	return &BlockServerMemory{
    44  		log, sync.RWMutex{}, make(map[kbfsblock.ID]blockMemEntry),
    45  	}
    46  }
    47  
    48  var errBlockServerMemoryShutdown = errors.New("BlockServerMemory is shutdown")
    49  
    50  // FastForwardBackoff implements the BlockServer interface.
    51  func (b *BlockServerMemory) FastForwardBackoff() {}
    52  
    53  // Get implements the BlockServer interface for BlockServerMemory.
    54  func (b *BlockServerMemory) Get(
    55  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
    56  	context kbfsblock.Context, _ DiskBlockCacheType) (
    57  	data []byte, serverHalf kbfscrypto.BlockCryptKeyServerHalf, err error) {
    58  	if err := checkContext(ctx); err != nil {
    59  		return nil, kbfscrypto.BlockCryptKeyServerHalf{}, err
    60  	}
    61  
    62  	defer func() {
    63  		err = translateToBlockServerError(err)
    64  	}()
    65  	b.log.CDebugf(ctx, "BlockServerMemory.Get id=%s tlfID=%s context=%s",
    66  		id, tlfID, context)
    67  	b.lock.RLock()
    68  	defer b.lock.RUnlock()
    69  
    70  	if b.m == nil {
    71  		return nil, kbfscrypto.BlockCryptKeyServerHalf{},
    72  			errBlockServerMemoryShutdown
    73  	}
    74  
    75  	entry, ok := b.m[id]
    76  	if !ok {
    77  		return nil, kbfscrypto.BlockCryptKeyServerHalf{},
    78  			kbfsblock.ServerErrorBlockNonExistent{
    79  				Msg: fmt.Sprintf("Block ID %s does not exist.", id)}
    80  	}
    81  
    82  	if entry.tlfID != tlfID {
    83  		return nil, kbfscrypto.BlockCryptKeyServerHalf{},
    84  			fmt.Errorf("TLF ID mismatch: expected %s, got %s",
    85  				entry.tlfID, tlfID)
    86  	}
    87  
    88  	exists, _, err := entry.refs.checkExists(context)
    89  	if err != nil {
    90  		return nil, kbfscrypto.BlockCryptKeyServerHalf{}, err
    91  	}
    92  	if !exists {
    93  		return nil, kbfscrypto.BlockCryptKeyServerHalf{},
    94  			blockNonExistentError{id}
    95  	}
    96  
    97  	return entry.blockData, entry.keyServerHalf, nil
    98  }
    99  
   100  // GetEncodedSizes implements the BlockServer interface for
   101  // BlockServerMemory.
   102  func (b *BlockServerMemory) GetEncodedSizes(
   103  	ctx context.Context, tlfID tlf.ID, ids []kbfsblock.ID,
   104  	contexts []kbfsblock.Context) (
   105  	sizes []uint32, statuses []keybase1.BlockStatus, err error) {
   106  	if err := checkContext(ctx); err != nil {
   107  		return nil, nil, err
   108  	}
   109  
   110  	defer func() {
   111  		err = translateToBlockServerError(err)
   112  	}()
   113  	b.log.CDebugf(ctx,
   114  		"BlockServerMemory.GetEncodedSizes ids=%s tlfID=%s contexts=%s",
   115  		ids, tlfID, contexts)
   116  	b.lock.RLock()
   117  	defer b.lock.RUnlock()
   118  
   119  	if b.m == nil {
   120  		return nil, nil, errBlockServerMemoryShutdown
   121  	}
   122  
   123  	sizes = make([]uint32, len(ids))
   124  	statuses = make([]keybase1.BlockStatus, len(ids))
   125  	for i, id := range ids {
   126  		entry, ok := b.m[id]
   127  		if !ok {
   128  			sizes[i] = 0
   129  			statuses[i] = keybase1.BlockStatus_UNKNOWN
   130  			continue
   131  		}
   132  
   133  		if entry.tlfID != tlfID {
   134  			return nil, nil, fmt.Errorf("TLF ID mismatch: expected %s, got %s",
   135  				entry.tlfID, tlfID)
   136  		}
   137  
   138  		context := contexts[i]
   139  		exists, refStatus, err := entry.refs.checkExists(context)
   140  		if err != nil {
   141  			return nil, nil, err
   142  		}
   143  		if !exists {
   144  			sizes[i] = 0
   145  			statuses[i] = keybase1.BlockStatus_UNKNOWN
   146  			continue
   147  		}
   148  
   149  		sizes[i] = uint32(len(entry.blockData))
   150  		statuses[i] = refStatus.toBlockStatus()
   151  	}
   152  
   153  	return sizes, statuses, nil
   154  }
   155  
   156  func validateBlockPut(checkNonzeroRef bool, id kbfsblock.ID, context kbfsblock.Context,
   157  	buf []byte) error {
   158  	var emptyID keybase1.UserOrTeamID
   159  	if context.GetCreator() == emptyID {
   160  		return fmt.Errorf("Can't Put() a block %v with an empty UID", id)
   161  	}
   162  
   163  	if context.GetCreator() != context.GetWriter() {
   164  		return fmt.Errorf("Can't Put() a block with creator=%s != writer=%s",
   165  			context.GetCreator(), context.GetWriter())
   166  	}
   167  
   168  	if checkNonzeroRef && context.GetRefNonce() != kbfsblock.ZeroRefNonce {
   169  		return errors.New("can't Put() a block with a non-zero refnonce")
   170  	}
   171  
   172  	return verifyLocalBlockIDMaybe(buf, id)
   173  }
   174  
   175  // doPut consolidates the put logic for implementing both the Put and PutAgain interface.
   176  func (b *BlockServerMemory) doPut(isRegularPut bool, tlfID tlf.ID, id kbfsblock.ID, context kbfsblock.Context,
   177  	buf []byte, serverHalf kbfscrypto.BlockCryptKeyServerHalf) (err error) {
   178  	defer func() {
   179  		err = translateToBlockServerError(err)
   180  	}()
   181  	err = validateBlockPut(isRegularPut, id, context, buf)
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	b.lock.Lock()
   187  	defer b.lock.Unlock()
   188  
   189  	if b.m == nil {
   190  		return errBlockServerMemoryShutdown
   191  	}
   192  
   193  	var refs blockRefMap
   194  	if entry, ok := b.m[id]; ok {
   195  		// If the entry already exists, everything should be
   196  		// the same, except for possibly additional
   197  		// references.
   198  
   199  		if entry.tlfID != tlfID {
   200  			return fmt.Errorf(
   201  				"TLF ID mismatch: expected %s, got %s",
   202  				entry.tlfID, tlfID)
   203  		}
   204  
   205  		// We checked that buf hashes to id, so no need to
   206  		// check that it's equal to entry.data (since that was
   207  		// presumably already checked previously).
   208  
   209  		if isRegularPut && entry.keyServerHalf != serverHalf {
   210  			return fmt.Errorf(
   211  				"key server half mismatch: expected %s, got %s",
   212  				entry.keyServerHalf, serverHalf)
   213  		}
   214  
   215  		refs = entry.refs
   216  	} else {
   217  		data := make([]byte, len(buf))
   218  		copy(data, buf)
   219  		refs = make(blockRefMap)
   220  		b.m[id] = blockMemEntry{
   221  			tlfID:         tlfID,
   222  			blockData:     data,
   223  			keyServerHalf: serverHalf,
   224  			refs:          refs,
   225  		}
   226  	}
   227  
   228  	return refs.put(context, liveBlockRef, "")
   229  }
   230  
   231  // Put implements the BlockServer interface for BlockServerMemory.
   232  func (b *BlockServerMemory) Put(
   233  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
   234  	context kbfsblock.Context, buf []byte,
   235  	serverHalf kbfscrypto.BlockCryptKeyServerHalf,
   236  	_ DiskBlockCacheType) (err error) {
   237  	if err := checkContext(ctx); err != nil {
   238  		return err
   239  	}
   240  	b.log.CDebugf(ctx, "BlockServerMemory.Put id=%s tlfID=%s context=%s "+
   241  		"size=%d", id, tlfID, context, len(buf))
   242  
   243  	return b.doPut(true, tlfID, id, context, buf, serverHalf)
   244  }
   245  
   246  // PutAgain implements the BlockServer interface for BlockServerMemory.
   247  func (b *BlockServerMemory) PutAgain(
   248  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
   249  	context kbfsblock.Context, buf []byte,
   250  	serverHalf kbfscrypto.BlockCryptKeyServerHalf,
   251  	_ DiskBlockCacheType) (err error) {
   252  	if err := checkContext(ctx); err != nil {
   253  		return err
   254  	}
   255  	b.log.CDebugf(ctx, "BlockServerMemory.PutAgain id=%s tlfID=%s context=%s "+
   256  		"size=%d", id, tlfID, context, len(buf))
   257  
   258  	return b.doPut(false, tlfID, id, context, buf, serverHalf)
   259  }
   260  
   261  // AddBlockReference implements the BlockServer interface for BlockServerMemory.
   262  func (b *BlockServerMemory) AddBlockReference(ctx context.Context, tlfID tlf.ID,
   263  	id kbfsblock.ID, context kbfsblock.Context) (err error) {
   264  	if err := checkContext(ctx); err != nil {
   265  		return err
   266  	}
   267  
   268  	defer func() {
   269  		err = translateToBlockServerError(err)
   270  	}()
   271  	b.log.CDebugf(ctx, "BlockServerMemory.AddBlockReference id=%s "+
   272  		"tlfID=%s context=%s", id, tlfID, context)
   273  
   274  	b.lock.Lock()
   275  	defer b.lock.Unlock()
   276  
   277  	if b.m == nil {
   278  		return errBlockServerMemoryShutdown
   279  	}
   280  
   281  	entry, ok := b.m[id]
   282  	if !ok {
   283  		return kbfsblock.ServerErrorBlockNonExistent{
   284  			Msg: fmt.Sprintf("Block ID %s doesn't "+
   285  				"exist and cannot be referenced.", id)}
   286  	}
   287  
   288  	if entry.tlfID != tlfID {
   289  		return fmt.Errorf("TLF ID mismatch: expected %s, got %s",
   290  			entry.tlfID, tlfID)
   291  	}
   292  
   293  	return entry.refs.put(context, liveBlockRef, "")
   294  }
   295  
   296  func (b *BlockServerMemory) removeBlockReference(
   297  	tlfID tlf.ID, id kbfsblock.ID, contexts []kbfsblock.Context) (
   298  	int, error) {
   299  	b.lock.Lock()
   300  	defer b.lock.Unlock()
   301  
   302  	if b.m == nil {
   303  		return 0, errBlockServerMemoryShutdown
   304  	}
   305  
   306  	entry, ok := b.m[id]
   307  	if !ok {
   308  		// This block is already gone; no error.
   309  		return 0, nil
   310  	}
   311  
   312  	if entry.tlfID != tlfID {
   313  		return 0, fmt.Errorf("TLF ID mismatch: expected %s, got %s",
   314  			entry.tlfID, tlfID)
   315  	}
   316  
   317  	for _, context := range contexts {
   318  		err := entry.refs.remove(context, "")
   319  		if err != nil {
   320  			return 0, err
   321  		}
   322  	}
   323  	count := len(entry.refs)
   324  	if count == 0 {
   325  		delete(b.m, id)
   326  	}
   327  	return count, nil
   328  }
   329  
   330  // RemoveBlockReferences implements the BlockServer interface for
   331  // BlockServerMemory.
   332  func (b *BlockServerMemory) RemoveBlockReferences(ctx context.Context,
   333  	tlfID tlf.ID, contexts kbfsblock.ContextMap) (
   334  	liveCounts map[kbfsblock.ID]int, err error) {
   335  	if err := checkContext(ctx); err != nil {
   336  		return nil, err
   337  	}
   338  
   339  	defer func() {
   340  		err = translateToBlockServerError(err)
   341  	}()
   342  	b.log.CDebugf(ctx, "BlockServerMemory.RemoveBlockReference "+
   343  		"tlfID=%s contexts=%v", tlfID, contexts)
   344  	liveCounts = make(map[kbfsblock.ID]int)
   345  	for id, idContexts := range contexts {
   346  		count, err := b.removeBlockReference(tlfID, id, idContexts)
   347  		if err != nil {
   348  			return nil, err
   349  		}
   350  		liveCounts[id] = count
   351  	}
   352  	return liveCounts, nil
   353  }
   354  
   355  func (b *BlockServerMemory) archiveBlockReference(
   356  	tlfID tlf.ID, id kbfsblock.ID, context kbfsblock.Context) error {
   357  	b.lock.Lock()
   358  	defer b.lock.Unlock()
   359  
   360  	if b.m == nil {
   361  		return errBlockServerMemoryShutdown
   362  	}
   363  
   364  	entry, ok := b.m[id]
   365  	if !ok {
   366  		return kbfsblock.ServerErrorBlockNonExistent{
   367  			Msg: fmt.Sprintf("Block ID %s doesn't "+
   368  				"exist and cannot be archived.", id)}
   369  	}
   370  
   371  	if entry.tlfID != tlfID {
   372  		return fmt.Errorf("TLF ID mismatch: expected %s, got %s",
   373  			entry.tlfID, tlfID)
   374  	}
   375  
   376  	exists, _, err := entry.refs.checkExists(context)
   377  	if err != nil {
   378  		return err
   379  	}
   380  	if !exists {
   381  		return kbfsblock.ServerErrorBlockNonExistent{
   382  			Msg: fmt.Sprintf("Block ID %s (ref %s) "+
   383  				"doesn't exist and cannot be archived.",
   384  				id, context.GetRefNonce())}
   385  	}
   386  
   387  	return entry.refs.put(context, archivedBlockRef, "")
   388  }
   389  
   390  // ArchiveBlockReferences implements the BlockServer interface for
   391  // BlockServerMemory.
   392  func (b *BlockServerMemory) ArchiveBlockReferences(ctx context.Context,
   393  	tlfID tlf.ID, contexts kbfsblock.ContextMap) (err error) {
   394  	if err := checkContext(ctx); err != nil {
   395  		return err
   396  	}
   397  
   398  	defer func() {
   399  		err = translateToBlockServerError(err)
   400  	}()
   401  	b.log.CDebugf(ctx, "BlockServerMemory.ArchiveBlockReferences "+
   402  		"tlfID=%s contexts=%v", tlfID, contexts)
   403  
   404  	for id, idContexts := range contexts {
   405  		for _, context := range idContexts {
   406  			err := b.archiveBlockReference(tlfID, id, context)
   407  			if err != nil {
   408  				return err
   409  			}
   410  		}
   411  	}
   412  
   413  	return nil
   414  }
   415  
   416  // GetLiveBlockReferences implements the BlockServer interface for
   417  // BlockServerMemory.
   418  func (b *BlockServerMemory) GetLiveBlockReferences(
   419  	ctx context.Context, tlfID tlf.ID, contexts kbfsblock.ContextMap) (
   420  	liveCounts map[kbfsblock.ID]int, err error) {
   421  	if err := checkContext(ctx); err != nil {
   422  		return nil, err
   423  	}
   424  
   425  	defer func() {
   426  		err = translateToBlockServerError(err)
   427  	}()
   428  	b.log.CDebugf(ctx, "BlockServerMemory.GetLiveBlockReferences "+
   429  		"tlfID=%s contexts=%v", tlfID, contexts)
   430  
   431  	b.lock.Lock()
   432  	defer b.lock.Unlock()
   433  
   434  	if b.m == nil {
   435  		return nil, errBlockServerMemoryShutdown
   436  	}
   437  
   438  	liveCounts = make(map[kbfsblock.ID]int)
   439  	for id := range contexts {
   440  		count := 0
   441  		entry, ok := b.m[id]
   442  		if ok {
   443  			count = entry.refs.getLiveCount()
   444  		}
   445  		liveCounts[id] = count
   446  	}
   447  	return liveCounts, nil
   448  }
   449  
   450  // getAllRefsForTest implements the blockServerLocal interface for
   451  // BlockServerMemory.
   452  func (b *BlockServerMemory) getAllRefsForTest(
   453  	ctx context.Context, tlfID tlf.ID) (
   454  	map[kbfsblock.ID]blockRefMap, error) {
   455  	res := make(map[kbfsblock.ID]blockRefMap)
   456  	b.lock.RLock()
   457  	defer b.lock.RUnlock()
   458  
   459  	if b.m == nil {
   460  		return nil, errBlockServerMemoryShutdown
   461  	}
   462  
   463  	for id, entry := range b.m {
   464  		if entry.tlfID != tlfID {
   465  			continue
   466  		}
   467  		res[id] = entry.refs.deepCopy()
   468  	}
   469  	return res, nil
   470  }
   471  
   472  func (b *BlockServerMemory) numBlocks() int {
   473  	b.lock.RLock()
   474  	defer b.lock.RUnlock()
   475  	return len(b.m)
   476  }
   477  
   478  // IsUnflushed implements the BlockServer interface for BlockServerMemory.
   479  func (b *BlockServerMemory) IsUnflushed(ctx context.Context, tlfID tlf.ID,
   480  	_ kbfsblock.ID) (bool, error) {
   481  	b.lock.RLock()
   482  	defer b.lock.RUnlock()
   483  
   484  	if b.m == nil {
   485  		return false, errBlockServerMemoryShutdown
   486  	}
   487  
   488  	return false, nil
   489  }
   490  
   491  // Shutdown implements the BlockServer interface for BlockServerMemory.
   492  func (b *BlockServerMemory) Shutdown(ctx context.Context) {
   493  	b.lock.Lock()
   494  	defer b.lock.Unlock()
   495  	// Make further accesses error out.
   496  	b.m = nil
   497  }
   498  
   499  // RefreshAuthToken implements the BlockServer interface for BlockServerMemory.
   500  func (b *BlockServerMemory) RefreshAuthToken(_ context.Context) {}
   501  
   502  // GetUserQuotaInfo implements the BlockServer interface for BlockServerMemory.
   503  func (b *BlockServerMemory) GetUserQuotaInfo(ctx context.Context) (info *kbfsblock.QuotaInfo, err error) {
   504  	if err := checkContext(ctx); err != nil {
   505  		return nil, err
   506  	}
   507  
   508  	// Return a dummy value here.
   509  	return &kbfsblock.QuotaInfo{Limit: math.MaxInt64}, nil
   510  }
   511  
   512  // GetTeamQuotaInfo implements the BlockServer interface for BlockServerMemory.
   513  func (b *BlockServerMemory) GetTeamQuotaInfo(
   514  	ctx context.Context, _ keybase1.TeamID) (
   515  	info *kbfsblock.QuotaInfo, err error) {
   516  	if err := checkContext(ctx); err != nil {
   517  		return nil, err
   518  	}
   519  
   520  	// TODO: check team membership and return error if not a reader?
   521  
   522  	// Return a dummy value here.
   523  	return &kbfsblock.QuotaInfo{Limit: math.MaxInt64}, nil
   524  }