github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/bserver_remote.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  	"sync"
     9  	"time"
    10  
    11  	"github.com/keybase/backoff"
    12  	"github.com/keybase/client/go/kbfs/idutil"
    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/libkb"
    17  	"github.com/keybase/client/go/logger"
    18  	"github.com/keybase/client/go/protocol/keybase1"
    19  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    20  	"github.com/pkg/errors"
    21  	"golang.org/x/net/context"
    22  )
    23  
    24  const (
    25  	// BServerDefaultPingIntervalSeconds is the default interval on which the
    26  	// client should contact the block server.
    27  	BServerDefaultPingIntervalSeconds = 10
    28  	// BServerPingTimeout is how long to wait for a ping response
    29  	// before breaking the connection and trying to reconnect.
    30  	BServerPingTimeout = 30 * time.Second
    31  )
    32  
    33  // blockServerRemoteAuthTokenRefresher is a helper struct for
    34  // refreshing auth tokens and managing connections.
    35  type blockServerRemoteClientHandler struct {
    36  	kbCtx         Context
    37  	name          string
    38  	log           logger.Logger
    39  	deferLog      logger.Logger
    40  	csg           idutil.CurrentSessionGetter
    41  	authToken     *kbfscrypto.AuthToken
    42  	srvRemote     rpc.Remote
    43  	connOpts      rpc.ConnectionOpts
    44  	rpcLogFactory rpc.LogFactory
    45  	pinger        pinger
    46  
    47  	connMu sync.RWMutex
    48  	conn   *rpc.Connection
    49  	client keybase1.BlockInterface
    50  }
    51  
    52  func newBlockServerRemoteClientHandler(
    53  	kbCtx Context, initMode InitMode, name string, log logger.Logger,
    54  	signer kbfscrypto.Signer, csg idutil.CurrentSessionGetter,
    55  	srvRemote rpc.Remote,
    56  	rpcLogFactory rpc.LogFactory) *blockServerRemoteClientHandler {
    57  	deferLog := log.CloneWithAddedDepth(1)
    58  	b := &blockServerRemoteClientHandler{
    59  		name:          name,
    60  		log:           log,
    61  		deferLog:      deferLog,
    62  		csg:           csg,
    63  		srvRemote:     srvRemote,
    64  		rpcLogFactory: rpcLogFactory,
    65  		kbCtx:         kbCtx,
    66  	}
    67  
    68  	b.pinger = pinger{
    69  		name:    name,
    70  		doPing:  b.pingOnce,
    71  		timeout: BServerPingTimeout,
    72  		log:     log,
    73  	}
    74  
    75  	b.authToken = kbfscrypto.NewAuthToken(
    76  		signer, kbfsblock.ServerTokenServer, kbfsblock.ServerTokenExpireIn,
    77  		"libkbfs_bserver_remote", VersionString(), b)
    78  
    79  	constBackoff := backoff.NewConstantBackOff(RPCReconnectInterval)
    80  	firstConnectDelay := time.Duration(0)
    81  	if initMode.DelayInitialConnect() {
    82  		firstConnectDelay = libkb.RandomJitter(bserverFirstConnectDelay)
    83  	}
    84  	b.connOpts = rpc.ConnectionOpts{
    85  		DontConnectNow:                true, // connect only on-demand
    86  		WrapErrorFunc:                 libkb.WrapError,
    87  		TagsFunc:                      libkb.LogTagsFromContext,
    88  		ReconnectBackoff:              func() backoff.BackOff { return constBackoff },
    89  		DialerTimeout:                 dialerTimeout,
    90  		FirstConnectDelayDuration:     firstConnectDelay,
    91  		InitialReconnectBackoffWindow: func() time.Duration { return bserverReconnectBackoffWindow },
    92  	}
    93  	b.initNewConnection()
    94  	return b
    95  }
    96  
    97  func (b *blockServerRemoteClientHandler) initNewConnection() {
    98  	b.connMu.Lock()
    99  	defer b.connMu.Unlock()
   100  
   101  	if b.conn != nil {
   102  		b.conn.Shutdown()
   103  	}
   104  
   105  	b.conn = rpc.NewTLSConnectionWithDialable(
   106  		b.srvRemote, kbfscrypto.GetRootCerts(
   107  			b.srvRemote.Peek(), libkb.GetBundledCAsFromHost),
   108  		kbfsblock.ServerErrorUnwrapper{}, b, b.rpcLogFactory,
   109  		b.kbCtx.NewNetworkInstrumenter(keybase1.NetworkSource_REMOTE),
   110  		logger.LogOutputWithDepthAdder{Logger: b.log},
   111  		rpc.DefaultMaxFrameLength, b.connOpts,
   112  		libkb.NewProxyDialable(b.kbCtx.GetEnv()))
   113  	b.client = keybase1.BlockClient{Cli: b.conn.GetClient()}
   114  }
   115  
   116  func (b *blockServerRemoteClientHandler) reconnect() error {
   117  	b.connMu.Lock()
   118  	defer b.connMu.Unlock()
   119  
   120  	if b.conn != nil {
   121  		ctx, cancel := context.WithTimeout(
   122  			context.Background(), reconnectTimeout)
   123  		defer cancel()
   124  		return b.conn.ForceReconnect(ctx)
   125  	}
   126  
   127  	b.initNewConnection()
   128  	return nil
   129  
   130  }
   131  
   132  func (b *blockServerRemoteClientHandler) shutdown() {
   133  	if b.authToken != nil {
   134  		b.authToken.Shutdown()
   135  	}
   136  
   137  	b.connMu.Lock()
   138  	defer b.connMu.Unlock()
   139  
   140  	if b.conn != nil {
   141  		b.conn.Shutdown()
   142  	}
   143  
   144  	// cancel the ping ticker
   145  	b.pinger.cancelTicker()
   146  }
   147  
   148  func (b *blockServerRemoteClientHandler) getClient() keybase1.BlockInterface {
   149  	b.connMu.RLock()
   150  	defer b.connMu.RUnlock()
   151  	return b.client
   152  }
   153  
   154  type ctxBServerResetKeyType int
   155  
   156  const (
   157  	// ctxBServerResetKey identifies whether the current context has
   158  	// already passed through `BServerRemote.resetAuth`.
   159  	ctxBServerResetKey ctxBServerResetKeyType = iota
   160  )
   161  
   162  // resetAuth is called to reset the authorization on a BlockServer
   163  // connection.
   164  func (b *blockServerRemoteClientHandler) resetAuth(
   165  	ctx context.Context, c keybase1.BlockInterface) (err error) {
   166  	ctx = context.WithValue(ctx, ctxBServerResetKey, b.name)
   167  
   168  	defer func() {
   169  		b.deferLog.CDebugf(
   170  			ctx, "BlockServerRemote: resetAuth called, err: %#v", err)
   171  	}()
   172  
   173  	session, err := b.csg.GetCurrentSession(ctx)
   174  	if err != nil {
   175  		b.log.CDebugf(
   176  			ctx, "%s: User logged out, skipping resetAuth", b.name)
   177  		return nil
   178  	}
   179  
   180  	_, hasDeadline := ctx.Deadline()
   181  	if !hasDeadline {
   182  		var cancel context.CancelFunc
   183  		ctx, cancel = context.WithTimeout(ctx, reconnectTimeout)
   184  		defer cancel()
   185  	}
   186  
   187  	// request a challenge
   188  	challenge, err := c.GetSessionChallenge(ctx)
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	// get a new signature
   194  	signature, err := b.authToken.Sign(ctx, session.Name,
   195  		session.UID, session.VerifyingKey, challenge)
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	return c.AuthenticateSession(ctx, signature)
   201  }
   202  
   203  // RefreshAuthToken implements the AuthTokenRefreshHandler interface.
   204  func (b *blockServerRemoteClientHandler) RefreshAuthToken(
   205  	ctx context.Context) {
   206  	if v := ctx.Value(ctxBServerResetKey); v == b.name {
   207  		b.log.CDebugf(ctx, "Avoiding resetAuth recursion")
   208  		return
   209  	}
   210  
   211  	if err := b.resetAuth(ctx, b.client); err != nil {
   212  		b.log.CDebugf(ctx, "%s: error refreshing auth token: %v", b.name, err)
   213  	}
   214  }
   215  
   216  var _ kbfscrypto.AuthTokenRefreshHandler = (*blockServerRemoteClientHandler)(nil)
   217  
   218  // HandlerName implements the ConnectionHandler interface.
   219  func (b *blockServerRemoteClientHandler) HandlerName() string {
   220  	return b.name
   221  }
   222  
   223  // OnConnect implements the ConnectionHandler interface.
   224  func (b *blockServerRemoteClientHandler) OnConnect(ctx context.Context,
   225  	conn *rpc.Connection, client rpc.GenericClient, _ *rpc.Server) error {
   226  	// reset auth -- using client here would cause problematic recursion.
   227  	c := keybase1.BlockClient{Cli: client}
   228  	err := b.resetAuth(ctx, c)
   229  	if err != nil {
   230  		return err
   231  	}
   232  
   233  	// Start pinging.
   234  	b.pinger.resetTicker(BServerDefaultPingIntervalSeconds)
   235  	return nil
   236  }
   237  
   238  // OnConnectError implements the ConnectionHandler interface.
   239  func (b *blockServerRemoteClientHandler) OnConnectError(err error, wait time.Duration) {
   240  	b.log.Warning("%s: connection error: %v; retrying in %s", b.name, err, wait)
   241  	if b.authToken != nil {
   242  		b.authToken.Shutdown()
   243  	}
   244  	b.pinger.cancelTicker()
   245  	// TODO: it might make sense to show something to the user if this is
   246  	// due to authentication, for example.
   247  }
   248  
   249  // OnDoCommandError implements the ConnectionHandler interface.
   250  func (b *blockServerRemoteClientHandler) OnDoCommandError(err error, wait time.Duration) {
   251  	b.log.Warning("%s: DoCommand error: %v; retrying in %s", b.name, err, wait)
   252  }
   253  
   254  // OnDisconnected implements the ConnectionHandler interface.
   255  func (b *blockServerRemoteClientHandler) OnDisconnected(ctx context.Context,
   256  	status rpc.DisconnectStatus) {
   257  	if status == rpc.StartingNonFirstConnection {
   258  		b.log.CWarningf(ctx, "%s: disconnected", b.name)
   259  	}
   260  	if b.authToken != nil {
   261  		b.authToken.Shutdown()
   262  	}
   263  	b.pinger.cancelTicker()
   264  }
   265  
   266  // ShouldRetry implements the ConnectionHandler interface.
   267  func (b *blockServerRemoteClientHandler) ShouldRetry(rpcName string, err error) bool {
   268  	// Do not let connection.go's DoCommand retry any batch rpcs
   269  	// since batchDowngradeReferences already handles retries.
   270  	switch rpcName {
   271  	case "keybase.1.block.delReferenceWithCount":
   272  		return false
   273  	case "keybase.1.block.archiveReferenceWithCount":
   274  		return false
   275  	}
   276  	return kbfsblock.IsThrottleError(err)
   277  }
   278  
   279  // ShouldRetryOnConnect implements the ConnectionHandler interface.
   280  func (b *blockServerRemoteClientHandler) ShouldRetryOnConnect(err error) bool {
   281  	_, inputCanceled := err.(libkb.InputCanceledError)
   282  	return !inputCanceled
   283  }
   284  
   285  var _ rpc.ConnectionHandler = (*blockServerRemoteClientHandler)(nil)
   286  
   287  func (b *blockServerRemoteClientHandler) pingOnce(ctx context.Context) {
   288  	_, err := b.getClient().BlockPing(ctx)
   289  	if err == context.DeadlineExceeded {
   290  		b.log.CDebugf(
   291  			ctx, "%s: Ping timeout -- reinitializing connection", b.name)
   292  		if err = b.reconnect(); err != nil {
   293  			b.log.CDebugf(ctx, "reconnect error: %v", err)
   294  		}
   295  	} else if err != nil {
   296  		b.log.CDebugf(ctx, "%s: ping error %s", b.name, err)
   297  	}
   298  }
   299  
   300  func (b *blockServerRemoteClientHandler) fastForwardBackoff() {
   301  	b.connMu.RLock()
   302  	defer b.connMu.RUnlock()
   303  	b.conn.FastForwardConnectDelayTimer()
   304  }
   305  
   306  type blockServerRemoteConfig interface {
   307  	diskBlockCacheGetter
   308  	codecGetter
   309  	signerGetter
   310  	currentSessionGetterGetter
   311  	logMaker
   312  	initModeGetter
   313  }
   314  
   315  // BlockServerRemote implements the BlockServer interface and
   316  // represents a remote KBFS block server.
   317  type BlockServerRemote struct {
   318  	config       blockServerRemoteConfig
   319  	shutdownFn   func()
   320  	log          traceLogger
   321  	deferLog     traceLogger
   322  	blkSrvRemote rpc.Remote
   323  
   324  	putConn *blockServerRemoteClientHandler
   325  	getConn *blockServerRemoteClientHandler
   326  }
   327  
   328  // Test that BlockServerRemote fully implements the BlockServer interface.
   329  var _ BlockServer = (*BlockServerRemote)(nil)
   330  
   331  // NewBlockServerRemote constructs a new BlockServerRemote for the
   332  // given address.
   333  func NewBlockServerRemote(kbCtx Context, config blockServerRemoteConfig,
   334  	blkSrvRemote rpc.Remote, rpcLogFactory rpc.LogFactory) *BlockServerRemote {
   335  	log := config.MakeLogger("BSR")
   336  	deferLog := log.CloneWithAddedDepth(1)
   337  	bs := &BlockServerRemote{
   338  		config:       config,
   339  		log:          traceLogger{log},
   340  		deferLog:     traceLogger{deferLog},
   341  		blkSrvRemote: blkSrvRemote,
   342  	}
   343  	// Use two separate auth clients -- one for writes and one for
   344  	// reads.  This allows small reads to avoid getting trapped behind
   345  	// large asynchronous writes.  TODO: use some real network QoS to
   346  	// achieve better prioritization within the actual network.
   347  	bs.putConn = newBlockServerRemoteClientHandler(kbCtx, config.Mode(),
   348  		"BlockServerRemotePut", log, config.Signer(),
   349  		config.CurrentSessionGetter(), blkSrvRemote, rpcLogFactory)
   350  	bs.getConn = newBlockServerRemoteClientHandler(kbCtx, config.Mode(),
   351  		"BlockServerRemoteGet", log, config.Signer(),
   352  		config.CurrentSessionGetter(), blkSrvRemote, rpcLogFactory)
   353  
   354  	bs.shutdownFn = func() {
   355  		bs.putConn.shutdown()
   356  		bs.getConn.shutdown()
   357  	}
   358  	return bs
   359  }
   360  
   361  // For testing.
   362  func newBlockServerRemoteWithClient(kbCtx Context, config blockServerRemoteConfig,
   363  	client keybase1.BlockInterface) *BlockServerRemote {
   364  	log := config.MakeLogger("BSR")
   365  	deferLog := log.CloneWithAddedDepth(1)
   366  	bs := &BlockServerRemote{
   367  		config:   config,
   368  		log:      traceLogger{log},
   369  		deferLog: traceLogger{deferLog},
   370  		putConn: &blockServerRemoteClientHandler{
   371  			log:      log,
   372  			deferLog: deferLog,
   373  			client:   client,
   374  			kbCtx:    kbCtx,
   375  		},
   376  		getConn: &blockServerRemoteClientHandler{
   377  			log:      log,
   378  			deferLog: deferLog,
   379  			client:   client,
   380  			kbCtx:    kbCtx,
   381  		},
   382  	}
   383  	return bs
   384  }
   385  
   386  // FastForwardBackoff implements the BlockServerinterface for
   387  // BlockServerRemote.
   388  func (b *BlockServerRemote) FastForwardBackoff() {
   389  	b.getConn.fastForwardBackoff()
   390  	b.putConn.fastForwardBackoff()
   391  }
   392  
   393  // RemoteAddress returns the remote bserver this client is talking to
   394  func (b *BlockServerRemote) RemoteAddress() string {
   395  	return b.blkSrvRemote.String()
   396  }
   397  
   398  // RefreshAuthToken implements the AuthTokenRefreshHandler interface.
   399  func (b *BlockServerRemote) RefreshAuthToken(ctx context.Context) {
   400  	b.putConn.RefreshAuthToken(ctx)
   401  	b.getConn.RefreshAuthToken(ctx)
   402  }
   403  
   404  // Get implements the BlockServer interface for BlockServerRemote.
   405  func (b *BlockServerRemote) Get(
   406  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
   407  	context kbfsblock.Context, cacheType DiskBlockCacheType) (
   408  	buf []byte, serverHalf kbfscrypto.BlockCryptKeyServerHalf, err error) {
   409  	ctx = rpc.WithFireNow(ctx)
   410  	var res keybase1.GetBlockRes
   411  	b.log.LazyTrace(ctx, "BServer: Get %s", id)
   412  
   413  	// Once the block has been retrieved, cache it.
   414  	defer func() {
   415  		b.log.LazyTrace(ctx, "BServer: Get %s done (err=%v)", id, err)
   416  		if err != nil {
   417  			b.deferLog.CWarningf(
   418  				ctx, "Get id=%s tlf=%s context=%s sz=%d err=%v",
   419  				id, tlfID, context, len(buf), err)
   420  		} else {
   421  			// But don't cache it if it's archived data, except if
   422  			// it's going to the sync cache.  Blocks marked for the
   423  			// sync cache must be cached, otherwise prefetching will
   424  			// never complete.
   425  			if res.Status == keybase1.BlockStatus_ARCHIVED &&
   426  				cacheType != DiskBlockSyncCache {
   427  				return
   428  			}
   429  
   430  			b.deferLog.CDebugf(
   431  				ctx, "Get id=%s tlf=%s context=%s sz=%d",
   432  				id, tlfID, context, len(buf))
   433  			dbc := b.config.DiskBlockCache()
   434  			if dbc != nil {
   435  				// This used to be called in a goroutine to prevent
   436  				// blocking the `Get`. But we need this cached
   437  				// synchronously so prefetch operations can work
   438  				// correctly.  No need to log an error since `dbc`
   439  				// will already log it.
   440  				_ = dbc.Put(ctx, tlfID, id, buf, serverHalf, cacheType)
   441  			}
   442  		}
   443  	}()
   444  
   445  	arg := kbfsblock.MakeGetBlockArg(tlfID, id, context)
   446  	res, err = b.getConn.getClient().GetBlock(ctx, arg)
   447  	return kbfsblock.ParseGetBlockRes(res, err)
   448  }
   449  
   450  // GetEncodedSizes implements the BlockServer interface for BlockServerRemote.
   451  func (b *BlockServerRemote) GetEncodedSizes(
   452  	ctx context.Context, tlfID tlf.ID, ids []kbfsblock.ID,
   453  	contexts []kbfsblock.Context) (
   454  	sizes []uint32, statuses []keybase1.BlockStatus, err error) {
   455  	ctx = rpc.WithFireNow(ctx)
   456  	b.log.LazyTrace(ctx, "BServer: GetEncodedSizes %s", ids)
   457  	defer func() {
   458  		b.log.LazyTrace(
   459  			ctx, "BServer: GetEncodedSizes %s done (err=%v)", ids, err)
   460  		if err != nil {
   461  			b.deferLog.CWarningf(
   462  				ctx, "GetEncodedSizes ids=%s tlf=%s contexts=%s err=%v",
   463  				ids, tlfID, contexts, err)
   464  		} else {
   465  			b.deferLog.CDebugf(
   466  				ctx, "GetEncodedSizes ids=%s tlf=%s contexts=%s "+
   467  					"szs=%d statuses=%s",
   468  				ids, tlfID, contexts, sizes, statuses)
   469  		}
   470  	}()
   471  
   472  	arg, err := kbfsblock.MakeGetBlockSizesArg(tlfID, ids, contexts)
   473  	if err != nil {
   474  		return nil, nil, err
   475  	}
   476  	res, err := b.getConn.getClient().GetBlockSizes(ctx, arg)
   477  	if err != nil {
   478  		return nil, nil, err
   479  	}
   480  	if len(res.Sizes) != len(res.Statuses) {
   481  		return nil, nil, errors.Errorf(
   482  			"Unexpected return param slice size difference: "+
   483  				"len(sizes)=%d != len(statuses)=%d",
   484  			len(res.Sizes), len(res.Statuses))
   485  	}
   486  	sizes = make([]uint32, len(res.Sizes))
   487  	for i, size := range res.Sizes {
   488  		sizes[i] = uint32(size)
   489  	}
   490  	return sizes, res.Statuses, nil
   491  }
   492  
   493  // Put implements the BlockServer interface for BlockServerRemote.
   494  func (b *BlockServerRemote) Put(
   495  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
   496  	bContext kbfsblock.Context, buf []byte,
   497  	serverHalf kbfscrypto.BlockCryptKeyServerHalf,
   498  	cacheType DiskBlockCacheType) (err error) {
   499  	ctx = rpc.WithFireNow(ctx)
   500  	dbc := b.config.DiskBlockCache()
   501  	if dbc != nil {
   502  		err := dbc.Put(ctx, tlfID, id, buf, serverHalf, cacheType)
   503  		if err != nil {
   504  			return err
   505  		}
   506  	}
   507  	size := len(buf)
   508  	b.log.LazyTrace(ctx, "BServer: Put %s", id)
   509  	defer func() {
   510  		b.log.LazyTrace(ctx, "BServer: Put %s done (err=%v)", id, err)
   511  		if err != nil {
   512  			b.deferLog.CWarningf(
   513  				ctx, "Put id=%s tlf=%s context=%s sz=%d err=%v",
   514  				id, tlfID, bContext, size, err)
   515  		} else {
   516  			b.deferLog.CDebugf(
   517  				ctx, "Put id=%s tlf=%s context=%s sz=%d",
   518  				id, tlfID, bContext, size)
   519  		}
   520  	}()
   521  
   522  	arg := kbfsblock.MakePutBlockArg(tlfID, id, bContext, buf, serverHalf)
   523  	// Handle OverQuota errors at the caller
   524  	return b.putConn.getClient().PutBlock(ctx, arg)
   525  }
   526  
   527  // PutAgain implements the BlockServer interface for BlockServerRemote
   528  func (b *BlockServerRemote) PutAgain(
   529  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
   530  	bContext kbfsblock.Context, buf []byte,
   531  	serverHalf kbfscrypto.BlockCryptKeyServerHalf,
   532  	cacheType DiskBlockCacheType) (err error) {
   533  	ctx = rpc.WithFireNow(ctx)
   534  	dbc := b.config.DiskBlockCache()
   535  	if dbc != nil {
   536  		err := dbc.Put(ctx, tlfID, id, buf, serverHalf, cacheType)
   537  		if err != nil {
   538  			return err
   539  		}
   540  	}
   541  	size := len(buf)
   542  	b.log.LazyTrace(ctx, "BServer: Put %s", id)
   543  	defer func() {
   544  		b.log.LazyTrace(ctx, "BServer: Put %s done (err=%v)", id, err)
   545  		if err != nil {
   546  			b.deferLog.CWarningf(
   547  				ctx, "Put id=%s tlf=%s context=%s sz=%d err=%v",
   548  				id, tlfID, bContext, size, err)
   549  		} else {
   550  			b.deferLog.CDebugf(
   551  				ctx, "Put id=%s tlf=%s context=%s sz=%d",
   552  				id, tlfID, bContext, size)
   553  		}
   554  	}()
   555  
   556  	arg := kbfsblock.MakePutBlockAgainArg(tlfID, id, bContext, buf, serverHalf)
   557  	// Handle OverQuota errors at the caller
   558  	return b.putConn.getClient().PutBlockAgain(ctx, arg)
   559  }
   560  
   561  // AddBlockReference implements the BlockServer interface for BlockServerRemote
   562  func (b *BlockServerRemote) AddBlockReference(ctx context.Context, tlfID tlf.ID,
   563  	id kbfsblock.ID, context kbfsblock.Context) (err error) {
   564  	ctx = rpc.WithFireNow(ctx)
   565  	b.log.LazyTrace(ctx, "BServer: AddRef %s", id)
   566  	defer func() {
   567  		b.log.LazyTrace(ctx, "BServer: AddRef %s done (err=%v)", id, err)
   568  		if err != nil {
   569  			b.deferLog.CWarningf(
   570  				ctx, "AddBlockReference id=%s tlf=%s context=%s err=%v",
   571  				id, tlfID, context, err)
   572  		} else {
   573  			b.deferLog.CDebugf(
   574  				ctx, "AddBlockReference id=%s tlf=%s context=%s",
   575  				id, tlfID, context)
   576  		}
   577  	}()
   578  
   579  	arg := kbfsblock.MakeAddReferenceArg(tlfID, id, context)
   580  	// Handle OverQuota errors at the caller
   581  	return b.putConn.getClient().AddReference(ctx, arg)
   582  }
   583  
   584  // RemoveBlockReferences implements the BlockServer interface for
   585  // BlockServerRemote
   586  func (b *BlockServerRemote) RemoveBlockReferences(ctx context.Context,
   587  	tlfID tlf.ID, contexts kbfsblock.ContextMap) (liveCounts map[kbfsblock.ID]int, err error) {
   588  	ctx = rpc.WithFireNow(ctx)
   589  	// TODO: Define a more compact printout of contexts.
   590  	b.log.LazyTrace(ctx, "BServer: RemRef %v", contexts)
   591  	defer func() {
   592  		b.log.LazyTrace(ctx, "BServer: RemRef %v done (err=%v)", contexts, err)
   593  		if err != nil {
   594  			b.deferLog.CWarningf(ctx, "RemoveBlockReferences batch size=%d err=%v", len(contexts), err)
   595  		} else {
   596  			b.deferLog.CDebugf(ctx, "RemoveBlockReferences batch size=%d", len(contexts))
   597  		}
   598  	}()
   599  	doneRefs, err := kbfsblock.BatchDowngradeReferences(ctx, b.log, tlfID, contexts, false, b.putConn.getClient())
   600  	return kbfsblock.GetLiveCounts(doneRefs), err
   601  }
   602  
   603  // ArchiveBlockReferences implements the BlockServer interface for
   604  // BlockServerRemote
   605  func (b *BlockServerRemote) ArchiveBlockReferences(ctx context.Context,
   606  	tlfID tlf.ID, contexts kbfsblock.ContextMap) (err error) {
   607  	ctx = rpc.WithFireNow(ctx)
   608  	b.log.LazyTrace(ctx, "BServer: ArchiveRef %v", contexts)
   609  	defer func() {
   610  		b.log.LazyTrace(ctx, "BServer: ArchiveRef %v done (err=%v)", contexts, err)
   611  		if err != nil {
   612  			b.deferLog.CWarningf(ctx, "ArchiveBlockReferences batch size=%d err=%v", len(contexts), err)
   613  		} else {
   614  			b.deferLog.CDebugf(ctx, "ArchiveBlockReferences batch size=%d", len(contexts))
   615  		}
   616  	}()
   617  	_, err = kbfsblock.BatchDowngradeReferences(ctx, b.log, tlfID, contexts, true, b.putConn.getClient())
   618  	return err
   619  }
   620  
   621  // GetLiveBlockReferences implements the BlockServer interface for
   622  // BlockServerRemote.
   623  func (b *BlockServerRemote) GetLiveBlockReferences(
   624  	ctx context.Context, tlfID tlf.ID, contexts kbfsblock.ContextMap) (
   625  	liveCounts map[kbfsblock.ID]int, err error) {
   626  	return kbfsblock.GetReferenceCount(
   627  		ctx, tlfID, contexts, keybase1.BlockStatus_LIVE, b.getConn.getClient())
   628  }
   629  
   630  // IsUnflushed implements the BlockServer interface for BlockServerRemote.
   631  func (b *BlockServerRemote) IsUnflushed(
   632  	_ context.Context, _ tlf.ID, _ kbfsblock.ID) (
   633  	bool, error) {
   634  	return false, nil
   635  }
   636  
   637  // GetUserQuotaInfo implements the BlockServer interface for BlockServerRemote
   638  func (b *BlockServerRemote) GetUserQuotaInfo(ctx context.Context) (info *kbfsblock.QuotaInfo, err error) {
   639  	// This method called when kbfs process starts up. So if
   640  	// DelayInitialConnect() is set for the mode (usually means we're on
   641  	// mobile), don't set "fire now" in context, to avoid unintionally fast
   642  	// forwarding the delay timer for connecting to bserver.
   643  	if !b.config.Mode().DelayInitialConnect() {
   644  		ctx = rpc.WithFireNow(ctx)
   645  	}
   646  	b.log.LazyTrace(ctx, "BServer: GetUserQuotaInfo")
   647  	defer func() {
   648  		b.log.LazyTrace(ctx, "BServer: GetUserQuotaInfo done (err=%v)", err)
   649  	}()
   650  	res, err := b.getConn.getClient().GetUserQuotaInfo2(
   651  		ctx, false /* no TLFs */)
   652  	if err != nil {
   653  		return nil, err
   654  	}
   655  	return kbfsblock.QuotaInfoFromProtocol(res), nil
   656  }
   657  
   658  // GetTeamQuotaInfo implements the BlockServer interface for BlockServerRemote
   659  func (b *BlockServerRemote) GetTeamQuotaInfo(
   660  	ctx context.Context, tid keybase1.TeamID) (
   661  	info *kbfsblock.QuotaInfo, err error) {
   662  	ctx = rpc.WithFireNow(ctx)
   663  	b.log.LazyTrace(ctx, "BServer: GetTeamQuotaInfo")
   664  	defer func() {
   665  		b.log.LazyTrace(ctx, "BServer: GetTeamQuotaInfo done (err=%v)", err)
   666  	}()
   667  	arg := keybase1.GetTeamQuotaInfo2Arg{
   668  		Tid:            tid,
   669  		IncludeFolders: false,
   670  	}
   671  	res, err := b.getConn.getClient().GetTeamQuotaInfo2(ctx, arg)
   672  	if err != nil {
   673  		return nil, err
   674  	}
   675  	return kbfsblock.QuotaInfoFromProtocol(res), nil
   676  }
   677  
   678  // Shutdown implements the BlockServer interface for BlockServerRemote.
   679  func (b *BlockServerRemote) Shutdown(ctx context.Context) {
   680  	if b.shutdownFn != nil {
   681  		b.shutdownFn()
   682  	}
   683  	b.getConn.shutdown()
   684  	b.putConn.shutdown()
   685  }